29

I've encountered a situation which works but I dont understand why. If I name an element id="abc" and do console.log(abc) without setting it first, it gives me the HTML Object. Could anyone explain this behaviour?

Example

<h1 Id="abc" >abcdefghj</h1> 

<script>
  // without using document.getElementById
  console.log(abc); // Returns h1 element  
 </script>

I don't know why it's give me the whole element without using document.getElementById().

Martijn
  • 15,791
  • 4
  • 36
  • 68
Abhishek Kumar
  • 539
  • 9
  • 17
  • `id` attribute is defined at global scope. this is why overuse of id's is not considered as best practice – marmeladze Feb 10 '20 at 08:37
  • 1
    I think following question can be usefull [Do DOM tree elements with ids become global variables?](https://stackoverflow.com/questions/3434278/do-dom-tree-elements-with-ids-become-global-variables) – AbdurrahmanY Feb 10 '20 at 08:44
  • 1
    `abc` is **not** a string. `"abc"` is. `abc` is an identifier, and will be first looked up in local scope, then in global, and then in the window object. – Polygnome Feb 10 '20 at 18:52

1 Answers1

33

This is a legacy feature of the DOM API: basically, anything with an ID is accessible by its key in the window object, i.e. window[id].

In your case, abc is essentially window.abc, which is a reference to the element with the ID of abc (i.e. matching the #abc selector).

This is also a reason why IDs in a HTML document must be unique, and that browser behavior of handling duplicated ID is technically undefined by spec.


On a related note, because of this feature, it is dangerous to give your elements IDs that may override native functions/prototypes. Here is an example: <button id="submit"> when located in a form. When you try to programmatically submit the form using formElement.submit(), you actually run into an error, because now the formElement.submit actually refers to the nested button element with the ID of submit instead of the native method. You can try this out yourself here:

const myForm = document.getElementById('myForm');

const helloButton = document.getElementById('btn');
helloButton.addEventListener('click', () => {
  myForm.submit();
})
<form id="myForm">
  <input type="text" />
  <button id="submit">
    Submit form
  </button>
  <button id="btn" type="button">
    Click me to programmatically submit form
  </button>
</form>
Terry
  • 63,248
  • 15
  • 96
  • 118
  • 3
    Nice, I didn't know this legacy feature, we learn every day ! – Pierre Feb 10 '20 at 08:35
  • 1
    Note: IIRC this used to be unreliable but in HTML5 it is now standard. – user253751 Feb 10 '20 at 17:31
  • 1
    You might want to include another tidbit in your answer: `abc` is not, as claimed by the OP, a string (that would be `"abc"`), but it is an identifier, and as such it is treated as a name and name lookup happens. – Polygnome Feb 10 '20 at 18:54
  • @user253751 Being standardised for legacy compatibility reasons doesn't prevent it from being deprecated. – Bergi Feb 11 '20 at 00:12