javascript - Why does jQuery or a DOM method such as getElementById not find the element? -
what possible reasons document.getelementbyid
, $("#id")
or other dom method / jquery selector not finding elements?
example problems include:
jquery silently failing bind event handler, , standard dom method returning null
resulting in error:
uncaught typeerror: cannot set property '...' of null
the element trying find wasn’t in dom when script ran.
the position of dom-reliant script can have profound effect upon behavior. browsers parse html documents top bottom. elements added dom , scripts (generally) executed they're encountered. this means order matters. typically, scripts can't find elements appear later in markup because elements have yet added dom.
consider following markup; script #1 fails find <div>
while script #2 succeeds:
<script> console.log("script #1: %o", document.getelementbyid("test")); // null </script> <div id="test">test div</div> <script> console.log("script #2: %o", document.getelementbyid("test")); // <div id="test" ... </script>
so, should do? you've got few options:
option 1: move script
move script further down page, before closing body tag. organized in fashion, rest of document parsed before script executed:
<body> <button id="test">click me</button> <script> document.getelementbyid("test").addeventlistener("click", function() { console.log("clicked: %o", this); }); </script> </body><!-- closing body tag -->
note: placing scripts @ bottom considered best practice.
option 2: jquery's ready()
defer script until dom has been parsed, using ready()
:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script> $(document).ready(function() { $("#test").click(function() { console.log("clicked: %o", this); }); }); </script> <button id="test">click me</button>
note: bind domcontentloaded
or window.onload
each has caveats. jquery's ready()
delivers hybrid solution.
option 3: event delegation
delegated events have advantage can process events descendant elements added document @ later time.
when element raises event (provided it's bubbling event , nothing stops propagation), each parent in element's ancestry receives event well. allows attach handler existing element , sample events bubble descendants... added after handler attached. have check event see whether raised desired element and, if so, run our code.
jquery's on()
performs logic us. provide event name, selector desired descendant, , event handler:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script> $(document).on("click", "#test", function(e) { console.log("clicked: %o", this); }); </script> <button id="test">click me</button>
note: typically, pattern reserved elements didn't exist @ load-time or avoid attaching large amount of handlers. it's worth pointing out while i've attached handler document
(for demonstrative purposes), should select nearest reliable ancestor.
option 4: defer
attribute
use defer
attribute of <script>
.
[
defer
, boolean attribute,] set indicate browser script meant executed after document has been parsed.
<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script> <button id="test">click me</button>
for reference, here's code external script:
document.getelementbyid("test").addeventlistener("click", function(e){ console.log("clicked: %o", this); });
note: defer
attribute seems magic bullet but it's important aware of caveats...
1. defer
can used external scripts, i.e.: having src
attribute.
2. aware of browser support, i.e.: buggy implementation in ie < 10