A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

пре 4 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. title: You Don't Need jQuery!
  2. url: http://blog.garstasio.com/you-dont-need-jquery/events/
  3. hash_url: 5313a28c96bbd1aa04754c52522d952e
  4. <div class="entry">
  5. <p>In this fifth installment of "You Don't Need jQuery (anymore)", I'm going to talk about dealing with events in the browser without jQuery. As always, each section will cover the jQuery approach, followed by a solution using the native Web API instead. After reading this post on events, you should be confident enough to deal with events in your own project without using jQuery.</p>
  6. <p>As I've mentioned (many times) before, this blog is not about bad-mouthing jQuery. jQuery is, without a doubt, ubiquitous in the world of web development. In the earlier days of web development, jQuery was required to smooth out the significant implementation differences and bugs found in various browsers when dealing with the DOM and the Web API as a whole. Also, the Web API was quite primitive at the time, in some respects, and jQuery helped to make development a bit more intuitive.</p>
  7. <p>Browsers, and the Web API, have come a long way in the last several years. You can do a lot without jQuery, and I have mostly avoided jQuery in my new projects for the last several years. The jQuery approach may take less keystrokes, or look a bit more elegant in some cases. That's fine, but the point of this blog isn't to help you reduce the number of keystrokes, or write more beautiful code.</p>
  8. <p>My last few posts covered selecting elements, DOM manipulation, and ajax requests. In those instances, the Web API is fairly elegant, and not much is gained by using jQuery. However, when dealing with events, the Web API is admittedly lacking in the elegance and convenience departments, even in modern browsers in some cases. That's ok, because this blog is about helping you understand the Web API so that you can avoid blindly depending on jQuery to develop your web applications. When you rely on a library to develop a large part of your application, you had better understand how the low-level API works. Otherwise you will find troubleshooting unexpected behaviors that will inevitably pop up from time to time to be quite frustrating. Keep in mind that <a href="https://github.com/jquery/jquery/issues">jQuery has bugs of its own</a>.</p>
  9. <p><strong>This post on events will primarily focus on event handling in modern browsers. We're going to define modern browsers as anything newer (and including) Internet Explorer 9.</strong> This is a commonly accepted definition, in my experience. That said, this post on events will also illustrate how jQuery was especially important when Internet Explorer 8 was commonly supported, and how you you likely will want to consider pulling in a events library, or even jQuery, to assist if you are in the unusual and unfortunate position to require support for Internet Explorer 8 or older in a new web application.</p>
  10. <p>Keep in mind that the examples, descriptions, and comparisons below do not represent exhaustive references. This is all just something to help you understand what the Web API provides. Both jQuery and the Web API allow you to perform much more complex operations than the ones demonstrated here. I am merely supplying you with the basic knowledge/building blocks required to build something much more complex.</p>
  11. <ol>
  12. <li><a href="#sending-native-(dom)-events">Sending Native (DOM) Events</a></li>
  13. <li><a href="#sending-custom-events">Sending Custom Events</a></li>
  14. <li><a href="#listening-for-events">Listening For Events</a></li>
  15. <li><a href="#removing-event-handlers">Removing Event Handlers</a></li>
  16. <li><a href="#modifying-events">Modifying Events</a></li>
  17. <li><a href="#event-delgation">Event Delegation</a></li>
  18. <li><a href="#keyboard-events">Keyboard Events</a></li>
  19. <li><a href="#mouse-events">Mouse Events</a></li>
  20. <li><a href="#browser-load-events">Browser Load Events</a></li>
  21. <li><a href="#ancient-browser-support">Ancient Browser Support</a></li>
  22. <li><a href="#libraries-to-consider">Libraries to Consider</a></li>
  23. <li><a href="#next">Next in this Series</a></li>
  24. </ol>
  25. <h2 id="sending-native-(dom)-events">Sending Native (DOM) Events</h2>
  26. <p>We'll start out simple here with DOM events, that is, events that are defined as part of the <strong>D</strong>ocument <strong>O</strong>bject <strong>M</strong>odel in the W3C (<strong>W</strong>orld <strong>W</strong>ide <strong>W</strong>eb <strong>C</strong>onsortium) specification.</p>
  27. <p>Let's say we have an anchor tag, and we want to programmatically click it using JavaScript.</p>
  28. <h4 id="jquery">jQuery</h4>
  29. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">anchorElement</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
  30. </code></pre></div>
  31. <p>Well, that was pretty easy! If we <em>really</em> want to do this the jQuery way (for whatever reason) we need to wrap the element so we can access jQuery's API, of course.</p>
  32. <h4 id="web-api">Web API</h4>
  33. <p>The above code will work in any browser available today (even IE6). jQuery certainly doesn't help us here. The code follows the same intuitive syntax if we want to trigger some other DOM events, such as <code>focus</code> and <code>blur</code>, or <code>submit</code> on a <code>&lt;form&gt;</code>.</p>
  34. <h2 id="sending-custom-events">Sending Custom Events</h2>
  35. <p>We have an event, "my-custom-event" that we need to trigger. This event must bubble by default, and must be cancellable by a handler.</p>
  36. <h4 id="jquery">jQuery</h4>
  37. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'some-element'</span><span class="p">).</span><span class="nx">trigger</span><span class="p">(</span><span class="s1">'my-custom-event'</span><span class="p">);</span>
  38. </code></pre></div>
  39. <p>The above code will fire the custom event starting with the <code>someElement</code> element. The event will bubble up the DOM by default. jQuery actually walks the event up the DOM itself, triggering any jQuery-specific event handlers and/or functions on each element that correspond to the event name (such as <code>click()</code> for a "click" event).</p>
  40. <h4 id="web-api">Web API</h4>
  41. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span><span class="s1">'Event'</span><span class="p">);</span>
  42. <span class="nx">event</span><span class="p">.</span><span class="nx">initEvent</span><span class="p">(</span><span class="s1">'my-custom-event'</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="c1">//can bubble, and is cancellable</span>
  43. <span class="nx">someElement</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
  44. </code></pre></div>
  45. <p>The above code will work in all browsers. But, the <code>createEvent</code> method on <code>document</code> has, for the most part, been deprecated. All browsers, to my knowledge, continue to support it though.</p>
  46. <p>The more "modern" approach involves using the <code>CustomEvent</code> constructor:</p>
  47. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CustomEvent</span><span class="p">(</span><span class="s1">'my-custom-event'</span><span class="p">,</span> <span class="p">{</span><span class="nx">bubbles</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">cancelable</span><span class="o">:</span> <span class="kc">true</span><span class="p">});</span>
  48. <span class="nx">someElement</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
  49. </code></pre></div>
  50. <p>Unfortunately, the <code>CustomEvent</code> constructor is not supported in any version of Internet Explorer to date. This will work in all other modern browsers though. If you need to support Internet Explorer (and we all do) you will need fall back to the initial example in this section. For the most part, it is probably best to continue to use <code>createEvent</code> if it is supported by the browser. If newer browsers begin to remove <code>createEvent</code> from their implementation of the Web API, you should be able to easily detect this and use <code>CustomEvent</code> instead.</p>
  51. <h2 id="listening-for-events">Listening For Events</h2>
  52. <p>The syntax required to consume an event, DOM or custom, is very similar for modern browsers between the jQuery and native approaches. For this example, we will set up some code that will notify us when an element we're interested in has been clicked (either programmatically or via user interaction):</p>
  53. <h4 id="jquery">jQuery</h4>
  54. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someElement</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  55. <span class="c1">// TODO event handler logic</span>
  56. <span class="p">});</span>
  57. </code></pre></div>
  58. <p>You can also make use of the <code>click</code> method, which allows you to register an event handler if the first argument is a handler function:</p>
  59. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someElement</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  60. <span class="c1">// TODO event handler logic</span>
  61. <span class="p">});</span>
  62. </code></pre></div>
  63. <h4 id="web-api">Web API</h4>
  64. <p>As mentioned before, the syntax for registering an event handler using the native browser API in modern browsers (which includes IE9) is refreshingly similar to jQuery:</p>
  65. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someElement</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  66. <span class="c1">// TODO event handler logic</span>
  67. <span class="p">});</span>
  68. </code></pre></div>
  69. <p>Note that all elements (for the most part) inherit from the <code>Node</code> interface, which itself inherits from the <code>EventTarget</code> interface. The first version of Internet Explorer that includes support for the <code>addEventListener</code> method on the <code>EventTarget</code> interface is IE9.</p>
  70. <h2 id="removing-event-handlers">Removing Event Handlers</h2>
  71. <p>Whether you use jQuery or vanilla JavaScript, you must keep track of your original event handler function in order to un-register it. While jQuery provides a convenience method to remove "all" event handlers of a particular type from a specific element, it is important to understand that this will <strong>only</strong> remove any event handlers that were attached to that element via jQuery (ignoring any that may have been attached <em>directly</em> via <code>addEventListener</code>). This is due to the fact that the Web API does not provide any way to obtain a list of registered event handlers, nor does it provide a way to blindly remove all event handlers attached to an element.</p>
  72. <p>So, let's say we've previously attached the following click event handler to an element:</p>
  73. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">myEventHandler</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  74. <span class="c1">// handles the event...</span>
  75. <span class="p">}</span>
  76. </code></pre></div>
  77. <p>...and we now want to remove it:</p>
  78. <h4 id="jquery">jQuery</h4>
  79. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'some-element'</span><span class="p">).</span><span class="nx">off</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">myEventHandler</span><span class="p">);</span>
  80. </code></pre></div>
  81. <h4 id="web-api">Web API</h4>
  82. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someElement</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="nx">myEventHandler</span><span class="p">);</span>
  83. </code></pre></div>
  84. <h2 id="modifying-events">Modifying Events</h2>
  85. <p>The ability to modify an event generally refers to augmenting or squelching the event by one event handler before it reaches other event handlers.</p>
  86. <h3 id="preventing-the-event-from-bubbling-further-up-the-dom">Preventing the event from bubbling further up the DOM</h3>
  87. <p>Here, we want to ensure the event, caught by our event handler, will not reach any additional event handlers positioned on ancestor elements. This will stop the event from bubbling up the DOM.</p>
  88. <h4 id="jquery">jQuery</h4>
  89. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someEl</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'some-event'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  90. <span class="nx">event</span><span class="p">.</span><span class="nx">stopPropagation</span><span class="p">();</span>
  91. <span class="p">});</span>
  92. </code></pre></div>
  93. <h4 id="web-api">Web API</h4>
  94. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someEl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'some-event'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  95. <span class="nx">event</span><span class="p">.</span><span class="nx">stopPropagation</span><span class="p">();</span>
  96. <span class="p">});</span>
  97. </code></pre></div>
  98. <p>The syntax between jQuery and the modern Web API here is almost identical.</p>
  99. <h3 id="preventing-the-event-from-hitting-any-additional-handlers-attached-to-the-current-element">Preventing the event from hitting any additional handlers attached to the current element</h3>
  100. <p>Not only do we want to prevent this event from hitting any handlers bound to elements that are ancestors of this one, but we also want to ensure no other event handlers bound to this element are hit either. So, we want to prevent the event from hitting <em>any</em> further event handlers.</p>
  101. <h4 id="jquery">jQuery</h4>
  102. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someEl</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'some-event'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  103. <span class="nx">event</span><span class="p">.</span><span class="nx">stopImmediatePropagation</span><span class="p">();</span>
  104. <span class="p">});</span>
  105. </code></pre></div>
  106. <h4 id="web-api">Web API</h4>
  107. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someEl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'some-event'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  108. <span class="nx">event</span><span class="p">.</span><span class="nx">stopImmediatePropagation</span><span class="p">();</span>
  109. <span class="p">});</span>
  110. </code></pre></div>
  111. <p>Again, the syntax between jQuery and the modern Web API here is eerily similar.</p>
  112. <h3 id="preventing-the-event-from-triggering-an-action-defined-by-the-browser">Preventing the event from triggering an action defined by the browser</h3>
  113. <p>Let's say we have the following element:</p>
  114. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"http://fineuploader.com"</span><span class="nt">&gt;</span>Go to Fine Uploader<span class="nt">&lt;/a&gt;</span>
  115. </code></pre></div>
  116. <p>...and we want to prevent a click on this anchor from opening the associated page. This will involve adding a click handler to the anchor element and instructing the browser to <em>not</em> execute the its native/default action.</p>
  117. <h4 id="jquery">jQuery</h4>
  118. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someAnchor</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  119. <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
  120. <span class="p">});</span>
  121. </code></pre></div>
  122. <h4 id="web-api">Web API</h4>
  123. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someAnchor</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  124. <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
  125. <span class="p">});</span>
  126. </code></pre></div>
  127. <p>Are you seeing a pattern here? The syntax required to deal with events is starting to look about the same between jQuery and the Web API.</p>
  128. <h2 id="event-delegation">Event Delegation</h2>
  129. <p>Suppose you have a list filled with list items that are sensitive to mouse clicks. You could attach a click handler to each individual list item. However, this may be inefficient and slow down your page with a large number of list items. Suppose items are added to this list dynamically. Now attaching a new event handler to each new list item, as it is added, becomes less appealing.</p>
  130. <p>The solution here is event delegation. That is, attach one click handler to the list. When any of the list items are clicked, the event will bubble up to its parent, the list container element. At this point, your one event handler will be hit and you can easily determine, by inspecting the event object, which list item was clicked and respond appropriately.</p>
  131. <p>The markup for such a list may look like this:</p>
  132. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;ul</span> <span class="na">id=</span><span class="s">"my-list"</span><span class="nt">&gt;</span>
  133. <span class="nt">&lt;li&gt;</span>foo <span class="nt">&lt;button&gt;</span>x<span class="nt">&lt;/button&gt;&lt;/li&gt;</span>
  134. <span class="nt">&lt;li&gt;</span>bar <span class="nt">&lt;button&gt;</span>x<span class="nt">&lt;/button&gt;&lt;/li&gt;</span>
  135. <span class="nt">&lt;li&gt;</span>abc <span class="nt">&lt;button&gt;</span>x<span class="nt">&lt;/button&gt;&lt;/li&gt;</span>
  136. <span class="nt">&lt;li&gt;</span>123 <span class="nt">&lt;button&gt;</span>x<span class="nt">&lt;/button&gt;&lt;/li&gt;</span>
  137. <span class="nt">&lt;/ul&gt;</span>
  138. </code></pre></div>
  139. <p>If the user clicks on the "x" button, the list item should be removed from the list.</p>
  140. <h4 id="jquery">jQuery</h4>
  141. <p>jQuery will set the context of our handler to the element that initially received the click (the <code>&lt;button&gt;</code>). Also, only <code>&lt;button&gt;</code> elements inside this list will be examined.</p>
  142. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'#my-list'</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="s1">'BUTTON'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  143. <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">parent</span><span class="p">().</span><span class="nx">remove</span><span class="p">();</span>
  144. <span class="p">});</span>
  145. </code></pre></div>
  146. <h4 id="web-api">Web API</h4>
  147. <p>We have to write a couple more lines of code without jQuery, but this still isn't exactly rocket science. The Web API always sets the context of the event handler to the element receiving the click event, which in this case is the list container element. Instead, we need to know which element was initially clicked, which is available in the <code>target</code> property of the provided <code>Event</code> object. We must also ensure that we only act on <code>&lt;button&gt;</code> clicks.</p>
  148. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'my-list'</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  149. <span class="kd">var</span> <span class="nx">clickedEl</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">;</span>
  150. <span class="k">if</span><span class="p">(</span><span class="nx">clickedEl</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="s1">'BUTTON'</span><span class="p">)</span> <span class="p">{</span>
  151. <span class="kd">var</span> <span class="nx">listItem</span> <span class="o">=</span> <span class="nx">clickedEl</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">;</span>
  152. <span class="nx">listItem</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">listItem</span><span class="p">);</span>
  153. <span class="p">}</span>
  154. <span class="p">});</span>
  155. </code></pre></div>
  156. <p>Remember, this isn't about elegant code, it's about exploring and understanding the Web API. After all, jQuery is just a Web API wrapper.</p>
  157. <h2 id="keyboard-events">Keyboard Events</h2>
  158. <p>In this section, I'll show how to handle various keyboard events. I'll also show how to identify which specific key was pressed by the user.</p>
  159. <p>First, we should take a moment to be sure we understand the difference between the three different types of general keyboard events:</p>
  160. <ol>
  161. <li><code>keydown</code>: Key has been pressed but not yet released. No default action has been performed yet.</li>
  162. <li><code>keypress</code>: Key has been pressed and a character has registered. This event will fire continuously if the key is held down.</li>
  163. <li><code>keyup</code>: Pressed key has been released.</li>
  164. </ol>
  165. <p>Let's say we are building a web application and want to make an interactive tutorial that we've build accessible via an intuitive keyboard shortcut. Anywhere in the application, our users should be able to pull up our help widget via the <code>Ctrl</code>-<code>H</code> key combination.</p>
  166. <h4 id="jquery">jQuery</h4>
  167. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">keydown</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  168. <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">ctrlKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">which</span> <span class="o">===</span> <span class="mi">72</span><span class="p">)</span> <span class="p">{</span>
  169. <span class="c1">// open help widget</span>
  170. <span class="p">}</span>
  171. <span class="p">});</span>
  172. </code></pre></div>
  173. <h4 id="web-api">Web API</h4>
  174. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'keydown'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  175. <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">ctrlKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">which</span> <span class="o">===</span> <span class="mi">72</span><span class="p">)</span> <span class="p">{</span>
  176. <span class="c1">// open help widget</span>
  177. <span class="p">}</span>
  178. <span class="p">});</span>
  179. </code></pre></div>
  180. <p>Nothing is obviously gained, in this instance, with jQuery. Even the syntax is almost identical between the Web API and jQuery.</p>
  181. <p>Registering for other keyboard events follows a similar pattern:</p>
  182. <h4 id="jquery">jQuery</h4>
  183. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nx">someElement</span><span class="p">).</span><span class="nx">keypress</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  184. <span class="c1">// ...</span>
  185. <span class="p">});</span>
  186. <span class="nx">$</span><span class="p">(</span><span class="nx">someElement</span><span class="p">).</span><span class="nx">keyup</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  187. <span class="c1">// ...</span>
  188. <span class="p">});</span>
  189. </code></pre></div>
  190. <h4 id="web-api">Web API</h4>
  191. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someElement</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'keypress'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  192. <span class="c1">// ...</span>
  193. <span class="p">});</span>
  194. <span class="nx">someElement</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'keyup'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  195. <span class="c1">// ...</span>
  196. <span class="p">});</span>
  197. </code></pre></div>
  198. <p>For more information about keyboard event properties and browser compatibility among the properties of this event, have a look at the <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent"><code>KeyboardEvent</code> document on Mozilla Developer Network</a>.</p>
  199. <h2 id="mouse-events">Mouse Events</h2>
  200. <p>There are a number of mouse events provided by the Web API, such as "mousedown", "mouseenter", and "mouseover" (to name a few). It isn't particularly interesting to show how to register for and handle these events in jQuery and the Web API. Much like keyboard events, the syntax between the two is almost identical.</p>
  201. <p>Instead, I'm going to focus on one special event that is part of jQuery's API. Of course, the goal here is to create your own code by using the plain 'ole Web API sans jQuery. I'll show you how to do that too.</p>
  202. <h3 id="jquery's-hover-event">jQuery's <code>hover</code> event</h3>
  203. <p>jQuery provides a way to notify you when a mouse pointer has hovered over a specific element, and then again when the mouse pointer leaves this element. For example:</p>
  204. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'some-element'</span><span class="p">).</span><span class="nx">hover</span><span class="p">(</span>
  205. <span class="kd">function</span> <span class="nx">hoverIn</span><span class="p">()</span> <span class="p">{</span>
  206. <span class="c1">// mouse is hovering over this element</span>
  207. <span class="p">},</span>
  208. <span class="kd">function</span> <span class="nx">hoverOut</span><span class="p">()</span> <span class="p">{</span>
  209. <span class="c1">// mouse was hovering over this element, but no longer is</span>
  210. <span class="p">}</span>
  211. <span class="p">);</span>
  212. </code></pre></div>
  213. <p>We can do the same thing with the Web API pretty easily:</p>
  214. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someEl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'mouseover'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  215. <span class="c1">// mouse is hovering over this element</span>
  216. <span class="p">});</span>
  217. <span class="nx">someEl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'mouseout'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  218. <span class="c1">// mouse was hovering over this element, but no longer is</span>
  219. <span class="p">});</span>
  220. </code></pre></div>
  221. <p>As I mentioned earlier, mouse events are fairly straightforward to handler with the Web API. For more information on mouse events, have a look at the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"><code>MouseEvent</code> interface documentation on the Mozilla Developer Network</a>.</p>
  222. <h2 id="browser-load-events">Browser Load Events</h2>
  223. <p>When talking about "load" events in the browser, we may be trying to answer any one of the following questions:</p>
  224. <h3 id="when-have-all-elements-on-the-page-fully-loaded-and-rendered-w/-applied-styles?">When have all elements on the page fully loaded and rendered w/ applied styles?</h3>
  225. <p>This event should be fired after:</p>
  226. <ul>
  227. <li>all markup has been placed on the page</li>
  228. <li>all stylesheets have been loaded</li>
  229. <li>all images have loaded</li>
  230. <li>all iframes have fully loaded</li>
  231. </ul>
  232. <h4 id="jquery">jQuery</h4>
  233. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">).</span><span class="nx">load</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  234. <span class="c1">// page is fully rendered</span>
  235. <span class="p">})</span>
  236. </code></pre></div>
  237. <h4 id="web-api">Web API</h4>
  238. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  239. <span class="c1">// page is fully rendered</span>
  240. <span class="p">});</span>
  241. </code></pre></div>
  242. <h3 id="when-has-all-static-markup-been-placed-on-the-page?">When has all static markup been placed on the page?</h3>
  243. <p>This event should fire after all markup has been placed on the page.</p>
  244. <h4 id="jquery">jQuery</h4>
  245. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  246. <span class="c1">// markup is on the page</span>
  247. <span class="p">});</span>
  248. </code></pre></div>
  249. <p>Note that you can also achieve the same thing in jQuery this way:</p>
  250. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  251. <span class="c1">// markup is on the page</span>
  252. <span class="p">});</span>
  253. </code></pre></div>
  254. <h4 id="web-api">Web API</h4>
  255. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'DOMContentLoaded'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  256. <span class="c1">// markup is on the page</span>
  257. <span class="p">});</span>
  258. </code></pre></div>
  259. <p>Note that you will likely want to ensure your script that registers the <code>DOMContentLoaded</code> event handler is placed before any stylesheet <code>&lt;link&gt;</code> tags, since loading these stylsheets will block any script execution and prolong the <code>DOMContentLoaded</code> event until the defined stylesheets have loaded.</p>
  260. <h3 id="when-has-a-particular-element-on-the-page-fully-loaded?">When has a particular element on the page fully loaded?</h3>
  261. <p>In addition to <code>window</code>, load events are associated with a number of elements, such as <code>&lt;img&gt;</code>, <code>&lt;link&gt;</code>, and <code>&lt;script&gt;</code>. The most common use of this event outside of <code>window</code> is to determine when a specific image has loaded.</p>
  262. <h4 id="jquery">jQuery</h4>
  263. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'img'</span><span class="p">).</span><span class="nx">load</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  264. <span class="c1">// image has successfully loaded</span>
  265. <span class="p">});</span>
  266. </code></pre></div>
  267. <p>You can also determine if the image has failed to load:</p>
  268. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'img'</span><span class="p">).</span><span class="nx">error</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  269. <span class="c1">// image has failed to load</span>
  270. <span class="p">});</span>
  271. </code></pre></div>
  272. <h4 id="web-api">Web API</h4>
  273. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">img</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  274. <span class="c1">// image has successfully loaded</span>
  275. <span class="p">});</span>
  276. </code></pre></div>
  277. <p>And if the image should fail to load?</p>
  278. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">img</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'error'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  279. <span class="c1">// image has failed to load</span>
  280. <span class="p">});</span>
  281. </code></pre></div>
  282. <p>As we've seen many times before, the syntax between jQuery and the Web API here for modern browsers is strikingly similar.</p>
  283. <h2 id="ancient-browser-support">Ancient Browser Support</h2>
  284. <p>This is where I talk about a time when jQuery was indeed a required library for web applications. Back when it was common to support IE8 or something even older. Back then, the Web/DOM API was a bit of a mess in some instances. This was especially true when dealing with events. Below, I'm going to briefly discuss some of the ways you can deal with events using vanilla JavaScript in older browsers. <strong>I'm going to limit this to IE7 and IE8.</strong> <a href="https://www.modern.ie/en-us/ie6countdown">IE6 is mostly dead at this point anyway</a>. Though many, if not all, of these examples should apply to IE6 as well.</p>
  285. <h3 id="listening-for-events">Listening For Events</h3>
  286. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">someElement</span><span class="p">.</span><span class="nx">attachEvent</span><span class="p">(</span><span class="s1">'onclick'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  287. <span class="c1">// TODO event handler logic</span>
  288. <span class="p">});</span>
  289. </code></pre></div>
  290. <p>You'll notice two distinct differences between this and the modern browser approach.</p>
  291. <ol>
  292. <li>We must rely on <code>attachEvent</code> instead of <code>addEventListener</code>.</li>
  293. <li>The event name must include a prefix of "on".</li>
  294. </ol>
  295. <p>Maybe you're wondering how you can easily and programmatically use the correct event handling method, based on the browser's capabilities. For most of us developing apps exclusively for modern browsers, this isn't a concern. But if, for some reason, you must target ancient browsers, such as IE8 (or older), you can use the following code to register for an event in any browser:</p>
  296. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">registerHandler</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">type</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
  297. <span class="kd">var</span> <span class="nx">listenerMethod</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">addEventListener</span> <span class="o">||</span> <span class="nx">target</span><span class="p">.</span><span class="nx">attachEvent</span><span class="p">,</span>
  298. <span class="nx">eventName</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">addEventListener</span> <span class="o">?</span> <span class="nx">type</span> <span class="o">:</span> <span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">;</span>
  299. <span class="nx">listenerMethod</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
  300. <span class="p">}</span>
  301. <span class="c1">// example use</span>
  302. <span class="nx">registerHandler</span><span class="p">(</span><span class="nx">someElement</span><span class="p">,</span> <span class="s1">'click'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  303. <span class="c1">// TODO event handler logic</span>
  304. <span class="p">});</span>
  305. </code></pre></div>
  306. <p>One more thing: if you want to remove an event handler in IE8 and older, you must use <code>detachEvent</code> instead of <code>removeEventListener</code>. So, a cross-browser way to remove an event listener may look like this:</p>
  307. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">unregisterHandler</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">type</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
  308. <span class="kd">var</span> <span class="nx">removeMethod</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">removeEventListener</span> <span class="o">||</span> <span class="nx">target</span><span class="p">.</span><span class="nx">detachEvent</span><span class="p">,</span>
  309. <span class="nx">eventName</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">removeEventListener</span> <span class="o">?</span> <span class="nx">type</span> <span class="o">:</span> <span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">;</span>
  310. <span class="nx">removeMethod</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
  311. <span class="p">}</span>
  312. <span class="c1">// example use</span>
  313. <span class="nx">unregisterHandler</span><span class="p">(</span><span class="nx">someElement</span><span class="p">,</span> <span class="s1">'click'</span><span class="p">,</span> <span class="nx">someEventHandlerFunction</span><span class="p">);</span>
  314. </code></pre></div>
  315. <h3 id="form-field-change-events">Form Field Change Events</h3>
  316. <p>Older versions of IE have some serious change-event deficiencies. Here are the two big ones that come to mind:</p>
  317. <ol>
  318. <li>Change events do not bubble.</li>
  319. <li>Checkboxes and radio buttons may not trigger a change event at all.</li>
  320. </ol>
  321. <p>It's important to know that the 2nd issue above was also reproducible when using jQuery with IE7/8 for quite a long time. As far as I can tell, current versions of jQuery (~1.10+) do properly address this issue though.</p>
  322. <p>To solve issue #1, you must attach a change handler directly to any form field that you'd like to monitor. Event delegation is not possible. For issue #2, you're best bet may be to attach a click handler to radio/checkbox fields instead of relying on the change event.</p>
  323. <h3 id="the-event-object">The Event Object</h3>
  324. <p>First off, in IE8 and older, the <code>Event</code> object is not passed directly to a registered event handler (remember, you must use <code>attachEvent</code>). Instead, the <code>Event</code> is set as a property on the <code>window</code>. So, if you are are writing an event handler that may be executed in modern <em>or</em> ancient browsers, you'll need something like this to cover both cases:</p>
  325. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">myEventHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  326. <span class="kd">var</span> <span class="nx">actualEvent</span> <span class="o">=</span> <span class="nx">event</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">event</span><span class="p">;</span>
  327. <span class="c1">// handle actualEvent</span>
  328. <span class="p">}</span>
  329. </code></pre></div>
  330. <p>Also, some properties of the <code>Event</code> object instance are a bit different in older browsers. For example, while the target of the event in modern browsers can be found by checking the <code>target</code> property of the <code>Event</code> instance, IE8 and older contain a different property for this element, named <code>srcElement</code>. So, now your cross-browser event handler looks like this:</p>
  331. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">myEventHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  332. <span class="kd">var</span> <span class="nx">actualEvent</span> <span class="o">=</span> <span class="nx">event</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">event</span><span class="p">,</span>
  333. <span class="nx">actualTarget</span> <span class="o">=</span> <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">target</span> <span class="o">||</span> <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">srcElement</span>
  334. <span class="c1">// handle actualEvent &amp; actualTarget</span>
  335. <span class="p">}</span>
  336. </code></pre></div>
  337. <p>In terms of controlling your events, the <code>stopPropagation</code> method is not available on an <code>Event</code> object instance in IE8 and older. If you want to stop an event from bubbling, you must instead set the <code>cancelBubble</code> property on the event. A cross-browser solution would look like this:</p>
  338. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">myEventHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
  339. <span class="kd">var</span> <span class="nx">actualEvent</span> <span class="o">=</span> <span class="nx">event</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">event</span><span class="p">;</span>
  340. <span class="k">if</span> <span class="p">(</span><span class="nx">actualEvent</span><span class="p">.</span><span class="nx">stopPropgation</span><span class="p">)</span> <span class="p">{</span>
  341. <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">stopPropagation</span><span class="p">();</span>
  342. <span class="p">}</span>
  343. <span class="k">else</span> <span class="p">{</span>
  344. <span class="nx">actualEvent</span><span class="p">.</span><span class="nx">cancelBubble</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  345. <span class="p">}</span>
  346. <span class="p">}</span>
  347. </code></pre></div>
  348. <p>IE8 and older also do not have a <code>stopImmediatePropagation</code> method. There isn't much to be done about this. I don't see lack of this method as a big loss, as use of <code>stopImmediatePropagation</code> seems like a code smell to me since the behavior of this call is completely dependent on the order that multiple event handlers are attached to the element in question.</p>
  349. <h3 id="browser-load-events">Browser Load Events</h3>
  350. <p>While <code>load</code> events on the <code>window</code> seem to function fairly well in ancient browsers, the <code>DOMContentLoaded</code> event was not supported by Internet Explorer until version 9. For older versions, the solution is a bit of a kludge. If <code>DOMContentLoaded</code> is really very important to you, and you must support IE8 and older, consider pulling in the tiny <a href="https://github.com/dperini/ContentLoaded/blob/master/src/contentloaded.js">contentloaded.js script</a>.</p>
  351. <p><a href="https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded#Cross-browser_fallback">Mozilla Developer Network provides a great explanation of the logic required to mimic the behavior of the <code>DOMContentLoaded</code> event in ancient browsers</a>:</p>
  352. <blockquote>
  353. <p>Internet Explorer 8 supports the readystatechange event, which can be used to detect when the DOM is ready. In earlier versions of Internet Explorer, this state can be detected by repeatedly trying to execute document.documentElement.doScroll("left");, as this snippet will throw an error until the DOM is ready.</p>
  354. </blockquote>
  355. <p>Of course, there are other gotchas in the context of event handling when dealing with ancient browsers. I've decided to outline a few of the more common ones above. If I forgot to mention something earth-shattering, please let me know in the comments.</p>
  356. <h2 id="libraries-to-consider">Libraries to Consider</h2>
  357. <p>You likely don't need a library to help you deal with events in modern browsers. But, if you really want some bells &amp; whistles:</p>
  358. <ul>
  359. <li>Consider using <a href="https://github.com/fat/bean">bean</a> for dealing with various event-related tasks. Bean was written by one of the original developers of Bootstrap.</li>
  360. <li><a href="http://craig.is/killing/mice">Mousetrap</a> is a nifty little library that makes it simple and fun to handle various keyboard shortcuts.</li>
  361. <li><a href="https://github.com/mroderick/PubSubJS">PubSubJS</a> is a small library that, as the readme states, focuses on topic-based publish/subscribe support.</li>
  362. </ul>
  363. <h2 id="next">Next</h2>
  364. <p>For me: I'll talk about various utility functions and tasks that are commonly needed when developing most web applications. This will be more of a (but not entirely) JavaScript-as-a-language focus. By contrast, most of my previous posts have focused almost exclusively on the Web/DOM API.</p>
  365. <p>For you: if I've left out any important event-related topics, please let me know in the comments so I can update this post.</p>
  366. </div>