Friday 27 January 2017

javascript - Why does jQuery or a DOM method such as getElementById not find the element?



What are the possible reasons for document.getElementById, $("#id") or any other DOM method / jQuery selector not finding the elements?



Example problems include:





  • jQuery silently failing to bind an event handler

  • jQuery "getter" methods (.val(), .html(), .text()) returning undefined

  • A standard DOM method returning null resulting in any of several errors:




Uncaught TypeError: Cannot set property '...' of null
Uncaught TypeError: Cannot read property '...' of null





The most common forms are:




Uncaught TypeError: Cannot set property 'onclick' of null



Uncaught TypeError: Cannot read property 'addEventListener' of null



Uncaught TypeError: Cannot read property 'style' of null




Answer



The element you were trying to find wasn’t in the DOM when your script ran.



The position of your DOM-reliant script can have a profound effect upon its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (generally) executed as they're encountered. This means that order matters. Typically, scripts can't find elements which appear later in the markup because those elements have yet to be added to the DOM.



Consider the following markup; script #1 fails to find the

while script #2 succeeds:






test div







So, what should you do? You've got a few options:








Move your script further down the page, just before the closing body tag. Organized in this fashion, the rest of the document is parsed before your script is executed:















Note: Placing scripts at the bottom is generally considered a best practice.








Defer your script until the DOM has been completely parsed, using ready():














Note: You could simply bind to DOMContentLoaded or window.onload but each has its caveats. jQuery's ready() delivers a hybrid solution.










Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.




When an element raises an event (provided that it's a bubbling event and nothing stops its propagation), each parent in that element's ancestry receives the event as well. That allows us to attach a handler to an existing element and sample events as they bubble up from its descendants... even those added after the handler is attached. All we have to do is check the event to see whether it was raised by the desired element and, if so, run our code.



jQuery's on() performs that logic for us. We simply provide an event name, a selector for the desired descendant, and an event handler:














Note: Typically, this pattern is reserved for elements which didn't exist at load-time or to avoid attaching a large amount of handlers. It's also worth pointing out that while I've attached a handler to document (for demonstrative purposes), you should select the nearest reliable ancestor.








Use the defer attribute of






For reference, here's the code from that external script:



document.getElementById("test").addEventListener("click", function(e){
console.log("clicked: %o", this);
});


Note: The defer attribute certainly seems like a magic bullet but it's important to be aware of the caveats...
1. defer can only be used for external scripts, i.e.: those having a src attribute.
2. be aware of browser support, i.e.: buggy implementation in IE < 10



No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...