Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The element you were trying to find wasn’t in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model" rel="noreferrer">DOM</a> when your script ran.</p> <p>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. <strong>This means that order matters.</strong> Typically, scripts can't find elements which appear later in the markup because those elements have yet to be added to the DOM.</p> <p>Consider the following markup; script #1 fails to find the <code>&lt;div&gt;</code> while script #2 succeeds:</p> <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code"> <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script&gt; console.log("script #1: %o", document.getElementById("test")); // null &lt;/script&gt; &lt;div id="test"&gt;test div&lt;/div&gt; &lt;script&gt; console.log("script #2: %o", document.getElementById("test")); // &lt;div id="test" ... &lt;/script&gt;</code></pre> </div> </div> </p> <p>So, what should you do? You've got a few options:</p> <hr> <h1>Option 1: Move your script</h1> <p>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:</p> <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code"> <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;body&gt; &lt;button id="test"&gt;click me&lt;/button&gt; &lt;script&gt; document.getElementById("test").addEventListener("click", function() { console.log("clicked: %o", this); }); &lt;/script&gt; &lt;/body&gt;&lt;!-- closing body tag --&gt;</code></pre> </div> </div> </p> <p><sub>Note: Placing scripts at the bottom is generally considered a <a href="http://developer.yahoo.com/performance/rules.html#js_bottom" rel="noreferrer">best practice</a>.</sub></p> <hr> <h1>Option 2: jQuery's <code>ready()</code></h1> <p>Defer your script until the DOM has been completely parsed, using <a href="http://api.jquery.com/ready/" rel="noreferrer"><code>ready()</code></a>:</p> <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code"> <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt; &lt;script&gt; $(document).ready(function() { $("#test").click(function() { console.log("clicked: %o", this); }); }); &lt;/script&gt; &lt;button id="test"&gt;click me&lt;/button&gt;</code></pre> </div> </div> </p> <p><sub>Note: You could simply bind to <a href="https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded" rel="noreferrer"><code>DOMContentLoaded</code></a> or <code>window.<a href="https://developer.mozilla.org/en-US/docs/Web/API/window.onload?redirect=no" rel="noreferrer">onload</a></code> but each has its caveats. jQuery's <a href="http://api.jquery.com/ready/" rel="noreferrer"><code>ready()</code></a> delivers a hybrid solution.</sub></p> <hr> <h1>Option 3: Event Delegation</h1> <blockquote> <p><a href="http://api.jquery.com/on/#direct-and-delegated-events" rel="noreferrer">Delegated events</a> have the advantage that they can process events from descendant elements that are added to the document at a later time.</p> </blockquote> <p>When an element raises an event (provided that it's a <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-bubbling" rel="noreferrer">bubbling</a> 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. </p> <p>jQuery's <a href="http://api.jquery.com/on/" rel="noreferrer"><code>on()</code></a> performs that logic for us. We simply provide an event name, a selector for the desired descendant, and an event handler:</p> <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code"> <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt; &lt;script&gt; $(document).on("click", "#test", function(e) { console.log("clicked: %o", this); }); &lt;/script&gt; &lt;button id="test"&gt;click me&lt;/button&gt;</code></pre> </div> </div> </p> <p><sub>Note: Typically, this pattern is reserved for elements which didn't exist at load-time <em>or</em> to avoid attaching a large amount of handlers. It's also worth pointing out that while I've attached a handler to <code>document</code> (for demonstrative purposes), you should select the nearest reliable ancestor.</sub></p> <hr> <h1>Option 4: The <code>defer</code> attribute</h1> <p>Use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer" rel="noreferrer"><code>defer</code></a> attribute of <code>&lt;script&gt;</code>.</p> <blockquote> <p>[<code>defer</code>, a Boolean attribute,] is set to indicate to a browser that the script is meant to be executed after the document has been parsed.</p> </blockquote> <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false"> <div class="snippet-code"> <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer&gt;&lt;/script&gt; &lt;button id="test"&gt;click me&lt;/button&gt;</code></pre> </div> </div> </p> <p>For reference, here's the code from that <a href="https://gh-canon.github.io/misc-demos/log-test-click.js" rel="noreferrer">external script</a>:</p> <pre><code>document.getElementById("test").addEventListener("click", function(e){ console.log("clicked: %o", this); }); </code></pre> <p><sub>Note: The <code>defer</code> attribute certainly <em>seems</em> like a magic bullet <em>but</em> it's important to be aware of the caveats...<br> 1. <code>defer</code> can only be used for external scripts, i.e.: those having a <code>src</code> attribute.<br> 2. be aware of <a href="http://caniuse.com/script-defer" rel="noreferrer">browser support</a>, i.e.: buggy implementation in IE &lt; 10</sub></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload