How does jQuery work when there are multiple elements with the same ID value?

HtmlJqueryIdentifier

Html Problem Overview


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
});

Html Solutions


Solution 1 - Html

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"]');

Solution 2 - Html

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.

Solution 3 - Html

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.

Solution 4 - Html

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

Solution 5 - Html

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.

Solution 6 - Html

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");

Solution 7 - Html

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);
});

Solution 8 - Html

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/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMisha MoroshkoView Question on Stackoverflow
Solution 1 - HtmlJoseph SilberView Answer on Stackoverflow
Solution 2 - Htmljfriend00View Answer on Stackoverflow
Solution 3 - HtmlAaronView Answer on Stackoverflow
Solution 4 - HtmlHéctor LazarteView Answer on Stackoverflow
Solution 5 - HtmlRalph LavelleView Answer on Stackoverflow
Solution 6 - HtmlBaqer NaqviView Answer on Stackoverflow
Solution 7 - HtmlMuhammad Azeem AbbasView Answer on Stackoverflow
Solution 8 - HtmlRavindra DhageView Answer on Stackoverflow