document.scripts is not equal to document.getElementsByTagName("script")
Asked Answered
P

2

7

document.scripts returns an HTMLCollection object holding a list of all scripts in a document. Similarly, document.getElementsByTagName("script") returns another HTMLCollection object holding a list of all scripts in the document.

I expected the following statement to be true however it's not.

document.scripts === document.getElementsByTagName("script") // false

What is the reason for both these not being equal?

Pyrethrum answered 10/10, 2020 at 8:58 Comment(5)
While both of them should return the same HTMLCollection, I think the ways those collections are filled are not the same. In other words, document.scripts is not an alias for document.getElementsByTagName('script') op. Weird why though, the definition in standard seems to make those equivalent. Perhaps that's the matter of being a legacy?Barnes
I just spotted another quirk of JavaScript...Pyrethrum
I don't even expect two calls to document.getElementsByTagName("script") would compare equal, well it turns out equal in my browser :OTaxicab
do you have some reference that they should be the same instance?Taxicab
@appleapple exactly, even I tried two calls to document.getElementsByTagName("script"), and they turned out the same.Pyrethrum
F
4

Here's an example where they give different results. document.scripts returns the set of HTMLElement scripts, where getElementsByTagName("script") returns the set of all scriot elements regardless of namespace.

(The StackSnippets logic adds two script elements to the document beyond those shown in the example code here.)

console.log('There are ' + document.scripts.length + ' document.scripts elements');
console.log('There are ' + document.getElementsByTagName("script").length + ' document.getElementsByTagName("script") elements');
<script>console.log('foo');</script>
<svg><script>console.log('bar');</script></svg>
Flatcar answered 10/10, 2020 at 13:0 Comment(2)
This means that document.scripts and document.getElementsByTagName("script") indeed represent two different collections. It's just that in most cases, they turn out to be the same.. Is this it?Pyrethrum
They turn out to have the same contents, yes.Flatcar
B
5

Well, I wasn't able to find the theoretical reasoning here, but it seems that in some cases those might not be strictly equivalent, and implementations have to bear that in mind. For example, in Chromium the implementations are different, and the real funny fact is that both are cached collections:

// renderer/core/dom/document.cc
HTMLCollection* Document::scripts() {
  return EnsureCachedCollection<HTMLCollection>(kDocScripts);
}

// renderer/core/dom/container_node.cc
HTMLCollection* ContainerNode::getElementsByTagName(
    const AtomicString& qualified_name) {
  DCHECK(!qualified_name.IsNull());

  if (GetDocument().IsHTMLDocument()) {
    return EnsureCachedCollection<HTMLTagCollection>(kHTMLTagCollectionType,
                                                     qualified_name);
  }
  return EnsureCachedCollection<TagCollection>(kTagCollectionType,
                                               qualified_name);
}

It's not just scripts: all the document HTMLCollection properties (forms, applets, images etc.) are wrapped into EnsureCachedCollection<HTMLCollection>.

For tags, however, it's EnsureCachedCollection<HTMLTagCollection>. While HTMLTagCollection is a child of TagCollection (which in turn is a child of HTMLCollection), those are different caches.

Now the way I expect caches to work, the same requests should give the same result... unless you hit different caches. In this case, same result means not just equality of values, but equivalence of objects.

That's why you get strict equivalence between both subsequent calls to document.getElementsByTagName('script') and subsequent calls to document.scripts - but not across those.

UPDATE: Check @Alohci answer, it gives a good example of when the results will actually be different by value for those requests.

Barnes answered 10/10, 2020 at 9:31 Comment(2)
so, basically, it's just about js rulesPyrethrum
Again, by the code alone it's more about the difference between different implementations of DOM: remember, HTML is not the only thing that's covered by that. But to be honest, I'm not really sure how document.scripts can be a TagCollection, for example... in WWW, legacy is really dark matter. )Barnes
F
4

Here's an example where they give different results. document.scripts returns the set of HTMLElement scripts, where getElementsByTagName("script") returns the set of all scriot elements regardless of namespace.

(The StackSnippets logic adds two script elements to the document beyond those shown in the example code here.)

console.log('There are ' + document.scripts.length + ' document.scripts elements');
console.log('There are ' + document.getElementsByTagName("script").length + ' document.getElementsByTagName("script") elements');
<script>console.log('foo');</script>
<svg><script>console.log('bar');</script></svg>
Flatcar answered 10/10, 2020 at 13:0 Comment(2)
This means that document.scripts and document.getElementsByTagName("script") indeed represent two different collections. It's just that in most cases, they turn out to be the same.. Is this it?Pyrethrum
They turn out to have the same contents, yes.Flatcar

© 2022 - 2024 — McMap. All rights reserved.