156

From what I understand the HTML5 spec lets you use IDs that are numbers like this.

<div id="1"></div>
<div id="2"></div>

I can access these fine using getElementById but not with querySelector. If I try do the following I get SyntaxError: DOM Exception 12 in the console.

document.querySelector("#1")

I'm just curious why using numbers as IDs does not work querySelector when the HTML5 spec says these are valid. I tried multiple browsers.

4
  • 1
    I don't think the HTML5 spec says they are valid. I'll double check... Nov 30, 2013 at 22:08
  • 6
    @beautifulcoder They are valid
    – dsgriffin
    Nov 30, 2013 at 22:08
  • 1
    Nevermind, according to validator.w3.org/check it is valid to use numbers. Maybe modern browsers haven't quite implemented the standard? Nov 30, 2013 at 22:15
  • not valid. "they cannot start with a digit, two hyphens, or a hyphen followed by a digit." source: w3.org/TR/CSS21/…. Perhaps you should prefix the selector with something semantic and valid related to the target. Sep 29, 2022 at 12:26

9 Answers 9

149

It is valid, but requires some special handling. From here: http://mathiasbynens.be/notes/css-escapes

Leading digits

If the first character of an identifier is numeric, you’ll need to escape it based on its Unicode code point. For example, the code point for the character 1 is U+0031, so you would escape it as \000031 or \31 .

Basically, to escape any numeric character, just prefix it with \3 and append a space character ( ). Yay Unicode!

So your code would end up as (CSS first, JS second):

#\31  {
    background: hotpink;
}

document.getElementById('1');
document.querySelector('#\\31 ');
3
  • 1
    What would the syntax be for values greater than 9? I can't get this to work with IDs like 10.
    – Berry Blue
    Dec 3, 2013 at 1:23
  • 8
    You need a space after the first character: #\\31 0 - you can refer to mothereffingcssescapes.com
    – Dennis
    Dec 3, 2013 at 23:41
  • 1
    Note that the space is only necessary if a character that is a hex digit immediately follows the escape sequence, in order to distinguish that character from the escape sequence. See w3.org/TR/CSS21/syndata.html#characters for more details.
    – BoltClock
    May 18, 2014 at 13:46
141

Because while they are valid in the HTML5 spec, they are not valid in CSS, which is what "query selector" means.

Instead, you would have to do this: document.querySelector("[id='1']"), which is very long-winded considering you could give it a meaningful ID like message1 or something ;)

5
  • 1
    You don't "have to" - there is a way to use an id selector with a leading digit. I agree though that it is better to have a meaningful id.
    – Dennis
    Nov 30, 2013 at 22:18
  • 15
    UUIDs can start by a number. Mar 15, 2018 at 12:54
  • 2
    If you have a list of ids, you can use document.querySelectorAll(myIds.map(id => `[id="${id}"]`).join(', '))
    – xystum
    Jan 3, 2021 at 16:05
  • Thank you very much!!! I spent a lot of time on "CSS Character Escape Sequence". This is the simple and elegant way. Aug 10, 2021 at 18:26
  • or document.querySelector('[id=\'1\']')
    – Alex78191
    Jun 30, 2022 at 9:00
55

I needed an approach which was automated. A recent change meant the id values used were no longer simple alphabetic characters and included numbers and special characters.

I ended up using CSS.escape: https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape

console.log(CSS.escape('1'));

First, this is the failing case:

const theId = "1";
document.querySelector(`#${theId}`);
const el = document.querySelector(`#${theId}`);
el.innerHTML = "After";
<div id="1">Before</div>

And now using CSS.escape:

const theId = "1";
const el = document.querySelector(`#${CSS.escape(theId)}`);
el.innerHTML = "After";
<div id="1">Before</div>

See how it correctly changes to show After, demonstrating the selector worked!

1
  • 1
    As of today, when you need to deal with some strange id you don't control, this the most clean solution, also because this is supported by all modern browser. Apr 18, 2020 at 19:43
7

Here is a function I made just now for dealing with leading number ID's in CSS selectors, and it is IE safe as CSS.escape is not.

Pass the selector through this cleanSelector function before using it :

var cleanSelector = function(selector){
    (selector.match(/(#[0-9][^\s:,]*)/g) || []).forEach(function(n){
        selector = selector.replace(n, '[id="' + n.replace("#", "") + '"]');
    });
    return selector;
};

var myselector = ".dog #980sada_as div span#aside:hover div.apple#05crab:nth-of-type(2), .ginger #2_green_div, div.cupcake #darwin p#23434-346365-53453";

var clean_myselector = cleanSelector(myselector);

// print to show difference
console.log(myselector);
console.log(clean_myselector);

//use the new selector like normal
var elems = document.querySelectorAll( clean_myselector ); 


3

From the W3C documentation Attribute selectors syntax

Attribute values must be a valid CSS identifiers or String.

Thus, digits or alphanumeric strings with leading digit does not qualify as a valid identifier.

If you are using an ID generator utility for generating an identifier, you might end up with alpha numeric ids with leading digits.

A quick fix would be to either omit digits from the SEED of the generator( if it can be modified ) or always append a string to the id generated.

2
  • 1
    "... always append a string to the id generated" do you mean "... always prepend an alphabetic string to the id generated"? Jun 7, 2021 at 23:11
  • Yes, you need to prepend. Jun 3, 2022 at 7:31
3

Using CSS.escape(invalidID) is promising solution.

Solution Video: https://youtu.be/rH5IVJKmSOE

In Your Case: document.querySelector("#" + CSS.escape('1'))

OR

document.querySelector("[id='1']")

1
  • Both of these methods have already been described in previous answers.
    – Besworks
    Jul 6, 2022 at 21:08
2

To use dynamic Ids in the query selector have used simple code.

this.template.querySelector("[id='"+divId+"']");

If you are working on the iterations and want to access on id then checkout this code. I had iteration on accounts object and using clickable event on LWC:

<template for:each={accounts.data} for:item="acc">
     <div key={acc.Id} id={acc.Id} class="box" onclick={handleClick}>
       {acc.Name}
     </div>
</template>

I wanted to access div data with using its ID when calling the onclick event on the div.

handleClick(event){
 event.dataTransfer.setData("divId", event.target.id);
 var divId = event.dataTransfer.getData("divId");

 //Using the divId in querySelector       
 var Element = this.template.querySelector("[id='"+divId+"']");

}
0

The CSS spec says:

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B&W?" or "B\26 W\3F".

https://www.w3.org/TR/CSS2/syndata.html#characters

0

Based on the existing answers, I made this little helper function to do the job for me:

export function QueryID(id: string): string {
    // Declare and initialize variables
    let firstChar: string = id.charAt(0);
    let isnumber: boolean = !isNaN(parseInt(firstChar));
    let sliceId: string;
    
    // If first char is number, escape it
    if (isnumber) {
        firstChar = CSS.escape(firstChar);
        sliceId = id.slice(1);
        id = firstChar.concat(sliceId);
    }

    // Return id
    return `#${id}`.toString();
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.