The DOM is a Mess @ Yahoo
Upcoming SlideShare
Loading in...5
×
 

The DOM is a Mess @ Yahoo

on

  • 51,167 views

A talk that I gave at Yahoo, in January 2009, about the DOM.

A talk that I gave at Yahoo, in January 2009, about the DOM.

Statistics

Views

Total Views
51,167
Views on SlideShare
30,341
Embed Views
20,826

Actions

Likes
84
Downloads
1,072
Comments
7

73 Embeds 20,826

http://ejohn.org 17825
http://ajaxian.com 1798
http://webhosting.pl 317
http://phpperformance.de 148
http://static.slideshare.net 111
http://axonflux.com 103
http://www.devexp.eu 78
http://jinyc.info 70
http://www.webhosting.pl 60
http://blog.juliencrouzet.fr 59
http://www.slideshare.net 57
http://www.techpresentations.org 16
http://api.jquery.com 15
http://www.hanrss.com 14
http://xss.yandex.net 13
http://lanyrd.com 12
http://www.taggle.org 11
http://feeds.feedburner.com 11
http://highheat.tistory.com 10
http://translate.googleusercontent.com 7
http://webcache.googleusercontent.com 6
http://127.0.0.1 5
http://10.0.140.244 4
http://www.agglom.com 4
http://www.netvibes.com 3
http://conversations.awarenessnetworks.com 3
http://extra.businesscatalyst.com 3
http://extjs.com 3
http://inf-proj.epoka.edu.al 3
http://extra.businesscatalyst.com 3
http://localhost 2
http://www.ifunt.com 2
http://xianguo.com 2
http://onwebdev.blogspot.com 2
http://blogspot.fluidnewmedia.com 2
http://wildfire.gigya.com 2
http://blog.elpengrod.com 2
http://feeds2.feedburner.com 2
http://www.ajax-blog.com 2
http://www.englishcafe.com 2
http://209.85.229.132 2
http://infrabel.local 1
http://rss2.com 1
http://www.iweb34.com 1
http://matthewjball.com 1
http://www.ajaxian.com 1
http://yoosuf.awardspace.com 1
https://www.techne.com.pl 1
http://htmledit.squarefree.com 1
http://blog.gabrieleromanato.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • dom
    Are you sure you want to
    Your message goes here
    Processing…
  • That's really amazing.

    Stulangperdana
    www.mdamin76.com/
    www.sprintringtones.org/
    Are you sure you want to
    Your message goes here
    Processing…
  • i really liked those! they were easy, convenient, and intuitive

    Regards
    Teisha
    http://winkhealth.com
    http://financewink.com
    http://www.fakhriramley.com
    Are you sure you want to
    Your message goes here
    Processing…
  • *cough* Yahoo has messed up DOM?
    I suggest your title could use some clarification. Given how we use Twitter there's nothing atoub '@yahoo' that suggests this was a presentation given at Yahoo ... if that is indeed what it means.
    Are you sure you want to
    Your message goes here
    Processing…
  • This stuff rocks, but just some questions:
    When a browser is released this bugs are caused due to bad reverse engeneering?
    I can't understand why the guys change de default behavior of javascript.
    Let's take PHP as an example, it's the same over all servers.
    Why is that so diferent from javascript or DOM on browers?
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

The DOM is a Mess @ Yahoo The DOM is a Mess @ Yahoo Presentation Transcript

  • The DOM is a Mess John Resig http://ejohn.org/ - http://twitter.com/jeresig/
  • A Tour of the DOM A messy DOM ✦ Writing Cross-Browser Code ✦ Common Features ✦ ✦ CSS Selector Engine ✦ DOM Modification ✦ Events
  • Messy Nearly every DOM method is broken in ✦ some way, in some browser. Some old: ✦ ✦ getElementById ✦ getElementsByTagName Some new: ✦ ✦ getElementsByClassName ✦ querySelectorAll
  • getElementById Likely the most commonly used DOM ✦ method A couple weird bits: ✦ ✦ IE and older versions of Opera returning elements with a name == id ✦ Does not easily work in XML documents
  • getElementsByTagName Likely tied for most-commonly-used ✦ DOM method Riddled with bugs in IE: ✦ ✦ “*” returns no elements in IE 5.5 ✦ “*” returns no elements on <object> elements in IE 7 ✦ .length gets overwritten in IE if an element with an ID=”length” is found
  • getElementsByClassName Landed in Firefox 3, Safari 3, Opera 9.6 ✦ A few knotty issues: ✦ ✦ HTMLElement.prototype .getElementsByClassName couldn’t be overwritten in Firefox ✦ Opera doesn’t match a second-specified class (e.g. class=”a b”, b isn’t found)
  • querySelectorAll Find DOM elements using CSS selectors ✦ In Firefox 3.1, Safari 3.1, Opera 10, IE 8 ✦ Birthing pains: ✦ ✦ Doesn’t exist in quirks mode, in IE 8 ✦ Safari 3.1 had memory out of bounds problems ✦ Safari 3.2 can’t match uppercase characters in quirks mode ✦ #id doesn’t match in XML documents
  • Moral If there’s a DOM method, there’s probably ✦ a problem with it somewhere, in some capacity.
  • Cross-Browser Code
  • Strategies Pick your browsers ✦ Know your enemies ✦ Write your code ✦
  • Cost / Benefit IE 7 IE 6 FF 3 Safari 3 Opera 9.5 Cost Benefit Draw a line in the sand.
  • Graded Support Yahoo Browser Compatibility
  • Browser Support Grid IE Firefox Safari Opera Chrome Previous 6.0 2.0 3.0 9.5 Current 7.0 3.0 3.2 9.6 Current Next 8.0 3.1 4.0 10.0 jQuery Browser Support
  • Browser Support Grid IE Firefox Safari Opera Chrome Previous 3.0 9.5 6.0 2.0 Current 7.0 3.0 3.2 9.6 Current Next 3.1 4.0 8.0 10.0 jQuery 1.3 Browser Support
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • Browser Bugs Generally your primary concern ✦ Your defense is a good test suite ✦ ✦ Prevent library regressions ✦ Analyze upcoming browser releases Your offense is feature simulation ✦ What is a bug? ✦ ✦ Is unspecified, undocumented, behavior capable of being buggy?
  • Test, Test, Test 1446 Tests, 9 browsers
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • External Code Making your code resistant to any ✦ environment ✦ Found through trial and error ✦ Integrate into your test suite ✦ Other libraries ✦ Strange code uses
  • Environment Testing 100% Passing: ✦ ✦ Standards Mode ✦ Quirks Mode ✦ Inline with Prototype + Scriptaculous ✦ Inline with MooTools Work in Progress: ✦ ✦ XHTML w/ correct mimetype ✦ With Object.prototype ✦ In XUL (Firefox Extensions) ✦ In Rhino (with Env.js)
  • Object.prototype  Object.prototype.otherKey = quot;otherValuequot;;      var obj = { key: quot;valuequot; };   for ( var prop in object ) {     if ( object.hasOwnProperty( prop ) ) {       assert( prop, quot;keyquot;,  quot;There should only be one iterated property.quot; );     }   } 
  • Greedy IDs  <form id=quot;formquot;>     <input type=quot;textquot; id=quot;lengthquot;/>     <input type=quot;submitquot; id=quot;submitquot;/>   </form>  document.getElementsByTagName(quot;inputquot;).length
  • Order of Stylesheets Putting stylesheets before code guarantees ✦ that they’ll load before the code runs. Putting them after can create an ✦ indeterminate situation.
  • Pollution Make sure your code doesn’t break ✦ outside code ✦ Use strict code namespacing ✦ Don’t extend outside objects, elements Bad: ✦ ✦ Introducing global variables ✦ Extending native objects (Array, Object) ✦ Extending DOM natives
  • Pollution http://mankz.com/code/ GlobalCheck.htm
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • Missing Features Typically older browsers missing specific ✦ features Optimal solution is to gracefully ✦ degrade ✦ Fall back to a simplified page Can’t make assumptions about ✦ browsers that you can’t support ✦ If it’s impossible to test them, you must provide a graceful fallback Object detection works well here. ✦
  • Object Detection Check to see if an object or property ✦ exists Useful for detecting an APIs existence ✦ Doesn’t test the compatibility of an API ✦ ✦ Bugs can still exist - need to test those separately with feature simulation
  • Event Binding  function attachEvent( elem, type, handle ) {     // bind event using proper DOM means     if ( elem.addEventListener )       elem.addEventListener(type, handle, false);            // use the Internet Explorer API     else if ( elem.attachEvent )       elem.attachEvent(quot;onquot; + type, handle);   } 
  • Fallback Detection  if ( typeof document !== quot;undefinedquot; &&         (document.addEventListener  || document.attachEvent) &&        document.getElementsByTagName &&  document.getElementById ) {     // We have enough of an API to  // work with to build our application   } else {     // Provide Fallback   }
  • Fallback Figure out a way to reduce the ✦ experience Opt to not execute any JavaScript ✦ ✦ Guarantee no partial API ✦ (e.g. DOM traversal, but no Events) Redirect to another page, or just work ✦ unobtrusively Working on a ready() fallback for jQuery ✦
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • Bug Fixes Don’t make assumptions about browser ✦ bugs. ✦ Assuming that a browser will always have a bug is foolhardy ✦ You will become susceptible to fixes ✦ Browsers will become less inclined to fix bugs Look to standards to make decisions about ✦ what are bugs
  • Failed Bug Fix in FF 3  // Shouldn't work   var node = documentA.createElement(quot;divquot;);   documentB.documentElement.appendChild( node );      // Proper way   var node = documentA.createElement(quot;divquot;);   documentB.adoptNode( node );   documentB.documentElement.appendChild( node ); 
  • Feature Simulation More advanced than object detection ✦ Make sure an API works as advertised ✦ Able to capture bug fixes gracefully ✦
  • Verify API  // Run once, at the beginning of the program   var ELEMENTS_ONLY = (function(){     var div = document.createElement(quot;divquot;);     div.appendChild( document.createComment(quot;testquot; ) );     return div.getElementsByTagName(quot;*quot;).length === 0;   })();      // Later on:   var all = document.getElementsByTagName(quot;*quot;);      if ( ELEMENTS_ONLY ) {     for ( var i = 0; i < all.length; i++ ) {       action( all[i] );     }   } else {     for ( var i = 0; i < all.length; i++ ) {       if ( all[i].nodeType === 1 ) {         action( all[i] );       }     }   } 
  • Figure Out Naming  <div id=quot;testquot; style=quot;color:red;quot;></div>   <div id=quot;test2quot;></div>   <script>   // Perform the initial attribute check   var STYLE_NAME = (function(){     var div = document.createElement(quot;divquot;);     div.style.color = quot;redquot;;          if ( div.getAttribute(quot;stylequot;) )       return quot;stylequot;;          if ( div.getAttribute(quot;cssTextquot;) )       return quot;cssTextquot;;   })();      // Later on:   window.onload = function(){     document.getElementsById(quot;test2quot;).setAttribute( STYLE_NAME,         document.getElementById(quot;testquot;).getAttribute( STYLE_NAME ) );   };   </script> 
  • Know Your Enemies Points of Concern for JavaScript Code Browser Bugs Regressions Missing Features JavaScript Code Bug Fixes External Code, Markup
  • Regressions Removing or changing unspecified APIs ✦ Object detection helps here ✦ Monitor upcoming browser releases ✦ ✦ All vendors provide access to beta releases ✦ Diligence! Example: IE 7 introduced ✦ XMLHttpRequest with file:// bug Test Suite Integration ✦
  • Object Failover  function attachEvent( elem, type, handle ) {     // bind event using proper DOM means     if ( elem.addEventListener )       elem.addEventListener(type, handle, false);            // use the Internet Explorer API     else if ( elem.attachEvent )       elem.attachEvent(quot;onquot; + type, handle);   } 
  • Safe Cross-Browser Fixes The easiest form of fix ✦ Unifies an API across browsers ✦ Implementation is painless ✦
  • Unify Dimensions  // ignore negative width and height values   if ( (key == 'width' || key == 'height') &&  parseFloat(value) < 0 )     value = undefined; 
  • Prevent Breakage  if ( name == quot;typequot; && elem.nodeName.toLowerCase()  == quot;inputquot; && elem.parentNode )     throw quot;type attribute can't be changedquot;; 
  • Untestable Problems Has an event handler been bound? ✦ Will an event fire? ✦ Do CSS properties like color or opacity ✦ actually affect the display? Problems that cause a browser crash. ✦ Problems that cause an incongruous API. ✦
  • Impractical to Test Performance-related issues ✦ Determining if Ajax requests will work ✦
  • Battle of Assumptions Cross-browser development is all about ✦ reducing the number of assumptions No assumptions indicates perfect code ✦ ✦ Unfortunately that’s an unobtainable goal ✦ Prohibitively expensive to write Have to draw a line at some point ✦
  • DOM Traversal Many methods of DOM traversal ✦ One unanimous solution: ✦ ✦ CSS Selector Engine
  • Traditional DOM getElementsByTagName ✦ getElementById ✦ getElementsByClassName ✦ ✦ in FF3, Safari 3, Opera 9.6 .children ✦ ✦ only returns elements (in all, and FF 3.1) getElementsByName ✦ .all[id] ✦ ✦ Match multiple elements by ID
  • Top-Down CSS Selector Traditional style of traversal ✦ ✦ Used by all major libraries Work from left-to-right ✦ “div p” ✦ ✦ Find all divs, find paragraphs inside Requires a lot of result merging ✦ And removal of duplicates ✦
  •    function find(selector, root){       root = root || document;              var parts = selector.split(quot; quot;),         query = parts[0],         rest = parts.slice(1).join(quot; quot;),         elems = root.getElementsByTagName( query ),         results = [];              for ( var i = 0; i < elems.length; i++ ) {         if ( rest ) {           results = results.concat( find(rest, elems[i]) );         } else {           results.push( elems[i] );         }       }              return results;     } 
  •  (function(){     var run = 0;          this.unique = function( array ) {       var ret = [];              run++;          for ( var i = 0, length = array.length; i < length; i++ ) {         var elem = array[ i ];            if ( elem.uniqueID !== run ) {           elem.uniqueID = run;           ret.push( array[ i ] );         }       }          return ret;     };   })(); 
  • Bottom-Up Work from right-to-left ✦ ✦ (How CSS Engines work in browsers.) “div p” ✦ ✦ Find all paragraphs, see if they have a div ancestor, etc. Fast for specific queries ✦ ✦ “div #foo” Deep queries get slow (in comparison) ✦ ✦ “#foo p”
  • Bottom-Up Some nice features: ✦ ✦ Only one DOM query ✦ No merge/unique required (except for “div, span”) ✦ Everything is a process of filtering
  •    function find(selector, root){       root = root || document;              var parts = selector.split(quot; quot;),         query = parts[parts.length - 1],         rest = parts.slice(0,-1).join(quot;quot;).toUpperCase(),         elems = root.getElementsByTagName( query ),         results = [];              for ( var i = 0; i < elems.length; i++ ) {         if ( rest ) {           var parent = elems[i].parentNode;           while ( parent && parent.nodeName != rest ) {             parent = parent.parentNode;           }                      if ( parent ) {             results.push( elems[i] );           }         } else {           results.push( elems[i] );         }       }              return results;     } 
  • CSS to XPath Browsers provide XPath functionality ✦ Collect elements from a document ✦ Works in all browsers ✦ ✦ In IE it only works on HTML documents Fast for a number of selectors (.class, “div ✦ div div”) ✦ Slow for some others: #id Currently used by Dojo and Prototype ✦
  •  if ( typeof document.evaluate === quot;functionquot; ) {     function getElementsByXPath(expression, parentElement) {       var results = [];       var query = document.evaluate(expression, parentElement || document,         null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);       for (var i = 0, length = query.snapshotLength; i < length; i++)         results.push(query.snapshotItem(i));       return results;     }   } 
  • Goal CSS 3 XPath All Elements * //* All P Elements p //p All Child Elements p>* //p/* Element By ID #foo //*[@id='foo'] //*[contains(concat(quot; quot;, Element By Class .foo @class, quot; quot;),quot; foo quot;)] Element With Attribute *[title] //*[@title] First Child of All P p > *:first-child //p/*[0] All P with an A descendant //p[a] Not possible Next Element p+* //p/following-sibling::*[0]
  • querySelectorAll The Selectors API spec from the W3C ✦ Two methods: ✦ ✦ querySelector (first element) ✦ querySelectorAll (all elements) Works on: ✦ ✦ document ✦ elements ✦ DocumentFragments Implemented in: ✦ ✦ Firefox 3.1, Safari 3, Opera 10, IE 8
  •  <div id=quot;testquot;>     <b>Hello</b>, I'm a ninja!   </div>   <div id=quot;test2quot;></div>   <script>   window.onload = function(){     var divs = document.querySelectorAll(quot;body > divquot;);     assert( divs.length === 2, quot;Two divs found using a CSS selector.quot; );          var b = document.getElementById(quot;testquot;).querySelector(quot;b:only-childquot;);     assert( b, quot;The bold element was found relative to another element.quot; );  };   </script> 
  •  <div id=quot;testquot;>     <b>Hello</b>, I'm a ninja!   </div>   <script>   window.onload = function(){     var b = document.getElementById(quot;testquot;).querySelector(quot;div bquot;);     assert( b, quot;Only the last part of the selector matters.quot; );  };   </script> 
  • DOM Modification Injecting HTML ✦ Removing Elements ✦
  • Injecting HTML HTML 5: ✦ insertAdjacentHTML Already in IE, dicey support, at best ✦ What can we use instead? ✦ ✦ We must generate our own HTML injection ✦ Use innerHTML to generate a DOM
  •  function getNodes(htmlString){     var map = {       quot;<tdquot;: [3, quot;<table><tbody><tr>quot;, quot;</tr></tbody></table>quot;],       quot;<optionquot;: [1, quot;<select multiple='multiple'>quot;, quot;</select>quot;]       // a full list of all element fixes     };          var name = htmlString.match(/<w+/),       node = name ? map[ name[0] ] || [0, quot;quot;, quot;quot;];          var div = document.createElement(quot;divquot;);     div.innerHTML = node[1] + htmlString + node[2];          while ( node[0]-- )       div = div.lastChild;          return div.childNodes;   }      assert( getNodes(quot;<td>test</td><td>test2</td>quot;).length === 2,     quot;Get two nodes back from the method.quot; );   assert( getNodes(quot;<td>test</td>quot;).nodeName === quot;TDquot;,     quot;Verify that we're getting the right node.quot; ); 
  • Element Mappings option and optgroup need to be contained in a ✦ <select multiple=quot;multiplequot;>...</select> legend need to be contained in a ✦ <fieldset>...</fieldset> thead, tbody, tfoot, colgroup, and caption need to be ✦ contained in a <table>...</table> tr need to be in a ✦ <table><thead>...</thead></table>, <table><tbody>...</tbody></table>, or a <table><tfoot>...</tfoot></table> td and th need to be in a ✦ <table><tbody><tr>...</tr></tbody></table> col in a ✦ <table><tbody></tbody><colgroup>...</ colgroup></table> link and script need to be in a ✦ div<div>...</div>
  • DocumentFragment Fragments can collect nodes ✦ Can be appended or cloned in bulk ✦ Super-fast (2-3x faster than normal) ✦
  •    function insert(elems, args, callback){       if ( elems.length ) {         var doc = elems[0].ownerDocument || elems[0],           fragment = doc.createDocumentFragment(),           scripts = getNodes( args, doc, fragment ),           first = fragment.firstChild;                if ( first ) {           for ( var i = 0; elems[i]; i++ ) {             callback.call( root(elems[i], first),                i > 0 ? fragment.cloneNode(true) : fragment );           }         }       }     }          var divs = document.getElementsByTagName(quot;divquot;);          insert(divs, [quot;Name:quot;], function(fragment){       this.appendChild( fragment );     });          insert(divs, [quot;First Lastquot;], function(fragment){       this.parentNode.insertBefore( fragment, this );     }); 
  • Inline Script Execution .append(“<script>var foo = 5;</script>”); ✦ Must execute scripts globally ✦ window.execScript() (for IE) ✦ eval.call( window, “var foo = 5;” ); ✦ Cross-browser way is to build a script ✦ element then inject it ✦ Executes globally
  •  function globalEval( data ) {     data = data.replace(/^s+|s+$/g, quot;quot;);          if ( data ) {       var head = document.getElementsByTagName(quot;headquot;)[0] || document.documentElement,         script = document.createElement(quot;scriptquot;);              script.type = quot;text/javascriptquot;;       script.text = data;              head.insertBefore( script, head.firstChild );       head.removeChild( script );     }   } 
  • Removing Elements Have to clean up bound events ✦ ✦ IE memory leaks Easy to do if it’s managed. ✦
  • Events Three big problems with Events: ✦ ✦ Memory Leaks (in IE) ✦ Maintaining ‘this’ (in IE) ✦ Fixing event object inconsistencies
  • Leaks Internet Explorer 6 leaks horribly ✦ ✦ Other IEs still leak, not so badly Attaching functions (that have a closure to ✦ another node) as properties Makes a leak: ✦ elem.test = function(){ anotherElem.className = “foo”; };
  • ‘this’ Users like having their ‘this’ refer to the ✦ target element Not the case in IE ✦ What’s the solution? ✦
  • Single Handler Bind a single handler to an event ✦ Call each bound function individually ✦ Can fix ‘this’ and event object ✦ How do we store the bound functions in a ✦ way that won’t leak?
  • Central Data Store Store all bound event handlers in a central ✦ object Link elements to handlers ✦ Keep good separation ✦ One data store per library instance ✦ Easy to manipulate later ✦ ✦ Trigger individual handlers ✦ Easy to remove again, later
  • Central Data Store Events Data Element function click(){} #43 function mouseover(){} Element Data Element #67 Element Events Element function click(){} #22 function click(){} Data Store
  • Multiple Stores Element #43 Library A Element #67 Element Element #37 Element #22 Library B
  • Unique Element ID The structure must hook to an element ✦ Elements don’t have unique IDs ✦ Must generate them and manage them ✦ jQuery.data( elem, “events” ); ✦ Unique attribute name: ✦ ✦ elem.jQuery123456789 = 45; ✦ Prevents collisions with other libraries We can store all sorts of data in here ✦
  • Questions? http://ejohn.org/ ✦ http://twitter.com/jeresig/ ✦