SitePoint Sponsor

User Tag List

Results 1 to 3 of 3
  1. #1
    SitePoint Addict
    Join Date
    Nov 2008
    Location
    Thailand
    Posts
    310
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    NodeList and objects toArray(). Advice needed.

    I've been working on a function to convert nodeList and object properties to an array.

    The first question is with regards IE and checking whether the object provided is an HTML collection.

    The best I have come up with so far, is to test if it's an object, has an 'item' which is a function and has length.

    Can this be improved upon?

    The second question is with regards slice.call and a while loop copy. I guess I need to do some profile/timing tests, but I'm wondering if the function merits a slice.call? or should I simplify?

    Any advice greatly appreciated.

    Code JavaScript:
    // Some weirdness in IE regarding nodesList and typeof 'item' returning 'Object'
    // even though when alerted it returns 'function (){...}'.
    // Therefore having to use regExp.test() to check whether it's a function instead.
    // Note: _isNodeList isn't full proof. An object with the properties 
    // {length: x, item : function(){}} will pass and return length.
    var _isNodeList = function(obj){
      var objType = {}.toString.call(obj);
      return (objType === '[object NodeList]' || 
              objType === '[object HTMLCollection]' ||
    		  objType === '[object Object]' && /^\s?function/.test(obj.item))
    		  && obj.length; // returns length of nodeList if true		  
    };
     
    var _toString = {}.toString,
        _slice = [].slice;
     
    // ------- toArray -------
    var toArray = function (obj /* HTMLCollection or Object */){
     
      var copy = [],
          len = _isNodeList(obj),
    	  oType = _toString.call(obj);
     
      if (len){
    	try {
          copy = _slice.call(obj); return copy;
        }
        catch(e) {
          while (len--) copy[len] = obj[len]; return copy;
        }
      } else if ( oType === '[object Object]' ){
    	for (var prop in obj){
    	  if (obj.hasOwnProperty(prop)) copy.push(obj[prop]);
        }
    	return copy;
      }
     
      throw new TypeError ( 'Object type ' + oType + ' not supported!!' ); 
    };

    Tests
    Code JavaScript:
    var obj = {length : 2, x : 3};
    alert(toArray(obj)); // [2,3]
     
    // An HTML unordered list.
    var liNodeList = document.getElementsByTagName('li');
    alert(toArray(liNodeList)); // [[object HTMLLIElement],[object HTMLLIElement],...,[object HTMLLIElement]]
     
    var liItems = toArray(liNodeList);
    try { liItems.forEach(function(el){ el.style.color = 'red'; }); } catch(e){}; // IE has no built-in array.forEach.
     
    var obj2 = {length : 2, item : function(){}};
    alert(toArray(obj2)); // [,] No Good!!

    ps. Oh and what's happened to crMalibu?

  2. #2
    SitePoint Guru
    Join Date
    Apr 2006
    Posts
    802
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I prefer a simpler method.

    Code:
    Array.from= function(what){
    	what= what || {};
    	var A= [], p, L= what.length;
    	if(typeof L== 'number'){
    		while(L) A[--L]= what[L];
    		return A;
    	}
    	for(p in what){
    		if(what.hasOwnProperty(p)) A[A.length]= what[p];
    	}
    	return A;
    }

  3. #3
    SitePoint Addict
    Join Date
    Nov 2008
    Location
    Thailand
    Posts
    310
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I prefer a simpler method.
    Fair enough but 'less strict' might have been better than 'simpler'

    Code:
    var fn = function(a,b,c){};
    var box = {height: 3, length: 2, width: 5}
    
    console.log(typeof fn.length === 'number') // true
    console.log(typeof box.length === 'number') // true
    
    console.log(Array.from(fn)); // [undefined,undefined,undefined]
    console.log(Array.from(box)); // [undefined,undefined]
    I did a quick test on the slice v loop scenario. Probably not that scientific but a few results

    Code JavaScript:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head><title>Slice Test</title></head>
    <body>
    <p id = 'slice'>Slice: </p>
    <p id = 'loop'>Loop: </p>
     
    <script type="text/javascript">
    var start, end, i = 0, x, len,
        a = [], 
        slice = [].slice,
    	sliceP = document.getElementById('slice'),
        loopP = document.getElementById('loop');
     
    for (;i < 10000; i++) a[i] = i;
     
    start = new Date();
    for (i = 0; i < 1000; i++){
      x = slice.call(a);
    }
    end = new Date() - start;
    sliceP.innerHTML += end + 'ms'; 
    // slice - FF 38ms Chrome 3ms IE 439ms Safari 224ms
     
     
    start = new Date();
    for (i = 0; i < 1000; i++){
      len = 10000;
      while(len) x[--len] = a[len];
    }
    end = new Date() - start;
    loopP.innerHTML += end + 'ms'; 
    // loop - FF 433ms Chrome 110ms IE 3227ms Safari 84ms
    </script>
    </body>
    </html>

    Safari threw a curve ball, but as far as FF, Chrome and IE it seems slice is considerably faster.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •