75

I fetch data from Google's AdWords website which has multiple elements with the same id.

Could you please explain why the following 3 queries doesn't result with the same answer (2)?

Live Demo

HTML:

<div>
    <span id="a">1</span>
    <span id="a">2</span>
    <span>3</span>
</div>

JS:

$(function() {
    var w = $("div");
    console.log($("#a").length);            // 1 - Why?
    console.log($("body #a").length);       // 2
    console.log($("#a", w).length);         // 2
});
DRosenfeld
  • 115
  • 1
  • 9
Misha Moroshko
  • 148,413
  • 200
  • 467
  • 700

8 Answers8

108

Having 2 elements with the same ID is not valid html according to the W3C specification.

When your CSS selector only has an ID selector (and is not used on a specific context), jQuery uses the native document.getElementById method, which returns only the first element with that ID.

However, in the other two instances, jQuery relies on the Sizzle selector engine (or querySelectorAll, if available), which apparently selects both elements. Results may vary on a per browser basis.

However, you should never have two elements on the same page with the same ID. If you need it for your CSS, use a class instead.


If you absolutely must select by duplicate ID, use an attribute selector:

$('[id="a"]');

Take a look at the fiddle: http://jsfiddle.net/P2j3f/2/

Note: if possible, you should qualify that selector with a tag selector, like this:

$('span[id="a"]');
Joseph Silber
  • 193,614
  • 53
  • 339
  • 276
  • 2
    Could you please explain the **Note**? When it is helpful to add the tag selector? – Misha Moroshko Dec 14 '11 at 03:11
  • 5
    @Misha Moroshko - Whenever possible, you should include a tag selector. The reason for this is because a tag selector is *much* more efficient than an attribute selector. If you qualify your attribute selector with a tag selector, jQuery will first use the tag selector to find the elements with that tag, and then only run the attribute selector on those elements. This is simply much more efficient. – Joseph Silber Dec 14 '11 at 04:33
  • 2
    I see here that `div#some_id` is slower than `#some_id`: http://stackoverflow.com/q/7262116/247243 – Misha Moroshko Dec 14 '11 at 07:55
  • 4
    @Misha Moroshko - That's because an ID selector is more efficient than a tag selector. But a tag selector is still faster than an attribute selector. – Joseph Silber Dec 14 '11 at 14:52
  • For what it's worth, ASP.NET Razor syntax automatically adds duplicate ids for things like radio buttons hence why I'm here! – Gareth Oct 01 '15 at 17:08
  • CSS selector engines vary, of course, but in general selectors are processed *right-to-left*, and adding qualifiers is not usually useful. For example: Adding a tag to an `[id=xyz]` selector has no beneficial effect on Chrome or Firefox (slightly detrimental if anything), although it does on IE11: https://jsfiddle.net/nhafkzko/ Mind you, selector engines are so blazingly fast here in 2016 (as compared with 2011, when they were merely *fast*) it really isn't likely to matter. – T.J. Crowder Jul 27 '16 at 11:34
  • @T.J.Crowder - AFAIK, compound selectors are not necessarily processed right-to-left. Only descendant selectors are. So `ul li` will process `li` before `ul`, but `li.foo` will not necessarily process `.foo` before `li`. – Joseph Silber Jul 28 '16 at 14:28
  • @JosephSilber: Yeah, you may well be right about that. Fundamentally, though, adding the tag really isn't necessary. – T.J. Crowder Jul 28 '16 at 14:44
  • thank you!! your solution saved my life!! – Felipe Coelho Jan 22 '21 at 15:05
6

There should only be one element with a given id. If you're stuck with that situation, see the 2nd half of my answer for options.

How a browser behaves when you have multiple elements with the same id (illegal HTML) is not defined by specification. You could test all the browsers and find out how they behave, but it's unwise to use this configuration or rely on any particular behavior.

Use classes if you want multiple objects to have the same identifier.

<div>
    <span class="a">1</span>
    <span class="a">2</span>
    <span>3</span>
</div>

$(function() {
    var w = $("div");
    console.log($(".a").length);            // 2
    console.log($("body .a").length);       // 2
    console.log($(".a", w).length);         // 2
});

If you want to reliably look at elements with IDs that are the same because you can't fix the document, then you will have to do your own iteration as you cannot rely on any of the built in DOM functions.

You could do so like this:

function findMultiID(id) {
    var results = [];
    var children = $("div").get(0).children;
    for (var i = 0; i < children.length; i++) {
        if (children[i].id == id) {
            results.push(children[i]);
        }
    }
    return(results);
}

Or, using jQuery:

$("div *").filter(function() {return(this.id == "a");});

jQuery working example: http://jsfiddle.net/jfriend00/XY2tX/.

As to Why you get different results, that would have to do with the internal implementation of whatever piece of code was carrying out the actual selector operation. In jQuery, you could study the code to find out what any given version was doing, but since this is illegal HTML, there is no guarantee that it will stay the same over time. From what I've seen in jQuery, it first checks to see if the selector is a simple id like #a and if so, just used document.getElementById("a"). If the selector is more complex than that and querySelectorAll() exists, jQuery will often pass the selector off to the built in browser function which will have an implementation specific to that browser. If querySelectorAll() does not exist, then it will use the Sizzle selector engine to manually find the selector which will have it's own implementation. So, you can have at least three different implementations all in the same browser family depending upon the exact selector and how new the browser is. Then, individual browsers will all have their own querySelectorAll() implementations. If you want to reliably deal with this situation, you will probably have to use your own iteration code as I've illustrated above.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
5

jQuery's id selector only returns one result. The descendant and multiple selectors in the second and third statements are designed to select multiple elements. It's similar to:

Statement 1

var length = document.getElementById('a').length;

...Yields one result.

Statement 2

var length = 0;
for (i=0; i<document.body.childNodes.length; i++) {
    if (document.body.childNodes.item(i).id == 'a') {
        length++;
    }
}

...Yields two results.

Statement 3

var length = document.getElementById('a').length + document.getElementsByTagName('div').length;

...Also yields two results.

Aaron
  • 4,862
  • 1
  • 16
  • 20
2

If you have multiple elements with same id or same name, just assign same class to those multiple elements and access them by index & perform your required operation.

  <div>
        <span id="a" class="demo">1</span>
        <span id="a" class="demo">2</span>
        <span>3</span>
    </div>

JQ:

$($(".demo")[0]).val("First span");
$($(".demo")[1]).val("Second span");
Baqer Naqvi
  • 4,058
  • 2
  • 37
  • 61
2

From the id Selector jQuery page:

Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM. This behavior should not be relied on, however; a document with more than one element using the same ID is invalid.

Naughty Google. But they don't even close their <html> and <body> tags I hear. The question is though, why Misha's 2nd and 3rd queries return 2 and not 1 as well.

Ralph Lavelle
  • 5,325
  • 4
  • 32
  • 44
0

Everybody says "Each id value must be used only once within a document", but what we do to get the elements we need when we have a stupid page that has more than one element with same id. If we use JQuery '#duplicatedId' selector we get the first element only. To achieve selecting the other elements you can do something like this

$("[id=duplicatedId]")

You will get a collection with all elements with id=duplicatedId

0

Access individual item

<div id='a' data-options='{"url","www.google.com"}'>Google</div>
<div id='a' data-options='{"url","www.facebook.com"}'>Facebook</div>
<div id='a' data-options='{"url","www.twitter.com"}'>Twitter</div>


$( "div[id='a']" ).on('click', function() {
$(location).attr('href', $(this).data('options').url);
});

-1

you can simply write $('span#a').length to get the length.

Here is the Solution for your code:

console.log($('span#a').length);

try JSfiddle: https://jsfiddle.net/vickyfor2007/wcc0ab5g/2/