Wednesday, September 21, 2005

一个很不错的js validate lib

http://jsval.fantastic-bits.de/ 意外收获:-) http://www.hotscripts.com/Ratings/index.html

Sunday, September 11, 2005

Browser Capabilities Test Page

浏览器兼容性测试页面 http://www.cyscape.com/showbrow.asp?q=goog-160&utm_source=google&utm_medium=cpc&utm_content=goog-160&utm_campaign=adwords&bhcp=1

Sunday, August 21, 2005

http://www.mistupid.com/internet/httpcodes.htm HTTP 1.1状态代码及其含义 prototype 源码解读 之 prototype.js http://www.bigbold.com/snippets/tag/javascript#post575

Javascript HashTable Implements

/**
   Created by: Michael Synovic
   on: 01/12/2003

   This is a Javascript implementation of the Java Hashtable object.

   Contructor(s):
    Hashtable()
             Creates a new, empty hashtable

   Method(s):
    void clear()
             Clears this hashtable so that it contains no keys.
    boolean containsKey(String key)
             Tests if the specified object is a key in this hashtable.
    boolean containsValue(Object value)
             Returns true if this Hashtable maps one or more keys to this value.
    Object get(String key)
             Returns the value to which the specified key is mapped in this hashtable.
    boolean isEmpty()
             Tests if this hashtable maps no keys to values.
    Array keys()
             Returns an array of the keys in this hashtable.
    void put(String key, Object value)
             Maps the specified key to the specified value in this hashtable. A NullPointerException is thrown is the key or value is null.
    Object remove(String key)
             Removes the key (and its corresponding value) from this hashtable. Returns the value of the key that was removed
    int size()
             Returns the number of keys in this hashtable.
    String toString()
             Returns a string representation of this Hashtable object in the form of a set of entries, enclosed in braces and separated by the ASCII characters ", " (comma and space).
    Array values()
             Returns a array view of the values contained in this Hashtable.
        
*/

function Hashtable()
   {
   this.clear = hashtable_clear;
   this.containsKey = hashtable_containsKey;
   this.containsValue = hashtable_containsValue;
   this.get = hashtable_get;
   this.isEmpty = hashtable_isEmpty;
   this.keys = hashtable_keys;
   this.put = hashtable_put;
   this.remove = hashtable_remove;
   this.size = hashtable_size;
   this.toString = hashtable_toString;
   this.values = hashtable_values;
   this.hashtable = new Array( );

   /*=======Private methods for internal use only - start========*/

   function hashtable_clear() {
       this.hashtable = new Array( );
       }//end hashtable_clear

   function hashtable_containsKey( key )
       {
       var exists = false;

       for (var i in this.hashtable)
           {
           if (i == key && this.hashtable[ i ] != null)
               {
               exists = true;
               break;
               }
           }

       return exists;
       }//end hashtable_containsKey

   function hashtable_containsValue( value )
       {
       var contains = false;

       if (value != null)
           {
           for (var i in this.hashtable)
               {
               if (this.hashtable[ i ] == value)
                   {
                   contains = true;
                   break;
                   }
               }
           }

       return contains;
       }//end hashtable_containsValue

   function hashtable_get( key ) {
       return this.hashtable[ key ];
       }//end hashtable_get

   function hashtable_isEmpty() {
       return ( parseInt( this.size( ) ) == 0 ) ? true : false;
       }//end hashtable_isEmpty

   function hashtable_keys()
       {
       var keys = new Array( );

       for (var i in this.hashtable)
           {
           if (this.hashtable[ i ] != null)
               keys.push( i );
           }

       return keys;
       }//end hashtable_keys

   function hashtable_put( key, value )
       {
       if (key == null || value == null) {
           throw "NullPointerException {" + key + "},{" + value + "}";
           } else {
           this.hashtable[ key ] = value;
           }
       }//end hashtable_put

   function hashtable_remove( key )
       {
       var rtn = this.hashtable[ key ];
       this.hashtable[ key ] = null;
       return rtn;
       }//end hashtable_remove

   function hashtable_size()
       {
       var size = 0;

       for (var i in this.hashtable)
           {
           if (this.hashtable[ i ] != null)
               size++;
           }

       return size;
       }// end hashtable_size

   function hashtable_toString()
       {
       var result = "";

       for (var i in this.hashtable)
           {
           if (this.hashtable[ i ] != null)
               result += "{" + i + "},{" + this.hashtable[ i ] + "}\n";
           }

       return result;
       }//end hashtable_toString

   function hashtable_values()
       {
       var values = new Array( );

       for (var i in this.hashtable)
           {
           if (this.hashtable[ i ] != null)
               values.push( this.hashtable[ i ] );
           }

       return values;
       }//end hashtable_values
     
        /*=======Private methods for internal use only - end ========*/

     
   }// end HashTable

Tuesday, August 16, 2005

javascript 资源收集

JavaScript Archive Network http://www.topshareware.com/008003-1-1.htm

Friday, August 12, 2005

javascript development tools

http://sourceforge.net/projects/jseditor/ I use JSEditor too, but I also like these two JS plugins : Harish Kataria's JavaScript Plug-in http://www.ee.ryerson.ca/~hkataria/ NRG JavaScript Editor http://www.energybyte.com/products/free%20products/

Wednesday, August 10, 2005

SitePoint Forums - Javascript - Usefull Tips & Tricks http://www.sitepoint.com/forums/showthread.php?p=2056400#post2056400
results from: Fxilla Firefox 1.06, Internet Explorer 6.0 (SP2), Opera 8.01 on Win XP SP2
 (can't test Safari since I don't have a Mac)
---
note:
 '~>' means "slightly faster"
 '>' means "faster" not "slower"
 '>>' means "much faster"
 '>>>' means "faster by several orders of magnitude"
note: I didn't record how fast each browser is relative to each other,
 since that is useless information for cross-browser optimization. But if you're interested:
 - the performance of the JS engines in all three are all comparable,
  though overall Opera is the fastest
  - Opera calls functions and concats strings much faster than the others
 - for DOM manipulation and access, Opera was by far the fastest, with Fx 2nd, and IE last
* innerHTML vs. W3C methods:
 - innerHTML is faster than appendChild/createElement
 - innerHTML is slower than appendChild/createTextNode
 - innerHTML is thus slower than appendChild
 - IE: appendChild and removeChild are relatively slow compared to createElement/cloneNode
 - Fx and Opera: appendChild and removeChild are relatively fast compared to createElement/cloneNode
* HTML DOM vs. XHTML/XML DOM:
 - case sensitivity:
  - HTML: tag names are returned uppercased (e.g. 'title' => 'TITLE')
  - HTML: attribute names are returned lowercased (e.g. 'ID' => 'id')
  - HTML: case-insensitive attribute values are returned lowercased (e.g. input's type attribute)
  - HTML: tag names and case-insensitive attribute values can be passed or set either upper- or lower-cased
  - XHTML: everything is case-sensitive
 - XHTML: doesn't support document.write or document.writeln
 - IE: doesn't support XHTML
* "ignorable" whitespace and comments:
 - "ignorable" whitespace or text nodes = whitespace-only text nodes that have no effect on the
  default presentation of the HTML document
 - IE:
  - DOM does not include comment nodes and "ignorable" text nodes before the first
   non-comment non-"ignorable"-text node
  - DOM does not include "ignorable" text nodes between element nodes
 - Fx and Opera: includes all "ignorable" whitespace
 [note to self: need to run more tests]
* accessing event object:
 (1) JS: some_element.onclick = foo;
  - Fx and Opera: event object is passed as sole parameter to foo
  - IE: event object is stored in window.event and NOT passed to foo
  - this object is the target of the event object
   (IE: event.srcElement; Fx and Opera (W3C): event.target)
  - addEventListener (Fx and Opera): event object is passed as sole parameter to addEventListener listeners
  - attachEvent (IE): event object is passed as sole parameter to attachEvent listeners
   and also stored in window.event
 (2) HTML: 
  - IE:
   - following function is assigned as onclick listener:
    function anonymous() {
     foo();
    }
   - event object is stored in window.event and NOT passed to foo
  - Fx and Opera:
   - following function is assigned as onclick listener:
    function anonymous(event) {
     foo();
    }
   - event object is passed to this function but NOT passed to foo
   - Fx: function name is actually "onclick", but that's irrelevant
  - in foo, this object refers to window object (and thus useless)
 (3) HTML: 
  - IE:
   - following function is assigned as onclick listener:
    function anonymous() {
     foo(event);
    }
   - event object is stored in window.event
   - identifier event resolves to window.event and is then passed to foo
  - Fx and Opera
   - following function is assigned as onclick listener:
    function anonymouse(event) {
     foo(event);
    }
   - event object is passed to this function as identifier event,
    which is then passed to foo
  - in foo, this object refers to window object (and thus useless)
  - other parameters can be passed, e.g. 
 - (1) and (3) are cross-browser
 - Fx: event object is truly passed down each listener during event propagation
 - IE: new event object is created for each listener during event propagation (verify this)
* computed style:
 - the computed style of an element is the combination of:
  - the element's style attribute
  - style rules from style elements that apply to the element
  - style rules linked external style sheets that apply to the element
 - IE: element.currentStyle
 - Fx and Opera: window.getComputedStyle(element, null)
 - cross-browser: element.currentStyle || window.getComputedStyle(element, null)
 - the returned object has the same properties names as element.style (e.g. computedStyle.display)
* "visibility: hidden" vs. "display: none":
 - "visibility: hidden"
  - offsetTop, offsetLeft:
   - Fx: (computed)
   - IE: (computed)
   - Opera: (computed)
  - offsetHeight, offsetWidth:
   - Fx: (computed)
   - IE: (computed)
   - Opera: (computed)
  - computedStyle.top, computedStyle.left:
   - Fx: (computed)
   - IE: (computed)
   - Opera: (computed)
  - computedStyle.height, computedStyle.width:
   - Fx: (computed)
   - IE: "auto"
   - Opera: (computed)
  - cannot be tabbed to
 - "display: none":
  - offsetTop, offsetLeft:
   - Fx: 0
   - IE: (computed)
   - Opera: 0
  - offsetHeight, offsetWidth:
   - Fx: 0
   - IE: 0
   - Opera: 0
  - computedStyle.top, computedStyle.left:
   - Fx: "0px"
   - IE: (computed)
   - Opera: (computed)
  - computedStyle.height, computedStyle.width:
   - Fx: "auto"
   - IE: "auto"
   - Opera: "0px"
  - cannot be tabbed to
* IE: setAttribute and getAttribute can use both "class" and "className" as parameters
 to refer to the class attribute
* setting event listener attributes:
 - in IE, a function must be passed: setAttribute('onclick', function() { foo(); })
 - in Fx and Opera (in accordance with W3C spec), a string must be passed: setAttribute('onclick', "foo();")
* IE bug: in a try/catch/finally statement, if an exception is thrown in the catch block, finally block does not execute
* creating new text nodes (createTextNode) is slightly faster than cloning empty text nodes and setting them
* creating elements:
 (1) document.createElement('div')
 (2) assume the following is defined:
   var div = document.createElement('div');
  div.cloneNode(false)
 (3) assume the following is defined:
   var div = document.createElement('div');
  div.cloneNode(true)
 - (2) and (3) are faster than (1)
 - (2) is as fast as (or negligably faster than) (3)
 - Fx, IE, and Opera summary: (2) == (3) > (1)
 - each element takes a different amount of time to be created;
  generally the more properties and methods an element has, the longer it takes;
  for example, creating an  vs. creating a 
- Fx: slightly slower - IE: nearly 10 times slower! - Opera: slower - recommend: (2) * creating document fragments: (1) document.createDocumentFragment() (2) assume the following is defined: var df = document.createDocumentFragment(); df.cloneNode(false) (3) assume the following is defined: var df = document.createDocumentFragment(); df.cloneNode(true) (4) (control case 1) assume the following is defined: var div = document.createElement('div') div.cloneNode(false) - Fx: (2) and (3) are slightly faster than (1) - Opera: (2) and (3) are much faster than (1) - Fx: (2) is as fast as (or negligably faster than) (3) - Opera: (2) is slightly faster than (3) - Fx and Opera: (2) and (3) are faster than (4) - IE: (2) is faster than (1) - IE: (3) is slower than (1) - IE: (4) is surprisingly much faster than (2) (creating/cloning document fragments is slower than cloning elements!) - Fx summary: (2) == (3) > (1) > (4) - IE summary: (4) >> (2) > (1) > (3) - Opera summary: (2) > (3) >> (1) > (4) - recommend: try not to use document fragments because of IE's sluggishness, but if they have to be used, use (2) * onload and onunload: (1) window.onload = func (2) (3) - all refer to the same thing, so setting one set the others - same thing with onunload * setting attributes: - Fx: if attribute doesn't exist, getAttribute returns null - IE and Opera: if attribute doesn't exist, getAttribute returns '' - getting and setting attribute properties are slightly faster than using get/setAttribute: for ex, anchor.href = 'url' is faster than anchor.setAttribute('href', 'url') - setAttribute(attrStr, null) does not do anything * iterating through children nodes: (1) for (var child = node.firstChild; child; child = child.nextSibling) (2) var childNodes = node.childNodes; var length = childNodes.length; for (var i = 0; i <> (node.childNodes and node.childNodes.length are cached into temporary vars, i.e. only resolved once), and (3) var childNodes = node.childNodes; for (var i = childNodes.length - 1; i >= 0; i--) (same as above, but node.childNodes.length no longer has to be cached) (4) for (var child = node.firstChild, i = 0; child; child = child.nextSibling, i++) - (4) is only slightly slower than (1), which indicates that incrementing i isn't expensive - no difference (or negligable) in speed between (2) and (3) - Fx: (2) and (3) are faster than (1) - IE: (1) is much faster than (2) and (3) - Opera: (1) is faster than (2) - Fx and IE: (2) is as fast as (3) - Opera: (3) is much slower than (2) - Fx summary: (2) == (3) > (1) - IE summary: (1) >> (2) == (3) - Opera summary: (1) > (2) >> (3) - recommend: (1) * dynamically making arrays: [previous analysis here was flawed] [note to self: repeat tests 16, 17] * adding to arrays: (1) array.push(x); (2) array[array.length] = x; - (1) is slower than (2) * concatenating strings: (1) for (var i = 0; i < len; i++) str1.concat(str_literal); (2) for (var i = 0; i < len; i++) str1 += str_literal; (3) for (var i = 0; i < len; i++) array.push(str_literal); str1 = array.join(''); (4) for (var i = 0, halflen = len >> 1; i < halflen; i++) str1 += str_literal + str_literal; - note on (3): this should be the fastest, since it's O(n) (linear), while the rest are O(n^2) (quadratic) - note on (4): this "unrolls" the loop to use + as well as += - checks to see if + is faster than += here (it is, as expected) - note: these tests concatenate string literals instead of string variables, but the results are the same, except for (5) - Fx: (2) == (3) == (4) ~> (1) - IE: (3) >>> (4) > (2) > (1) - IE: by far the slowest in string concatenation compared to Fx and Opera - Opera: (3) >> (4) >>> (2) ~> (1) - Opera: there seems to be a huge memory bug in Opera for (3), specifically array.join(): when the array approaches a certain size (~34000 strings of length 64) and array.join() is called, Opera starts to consume memory extremely quickly and freezes for several seconds or indefinitely - recommend: (3) since it's the fastest in IE, and the Opera problem only happens at extreme (and unrealistic) array sizes - adjacent string literals (1) 'abcdefghijklmnop' (2) 'a'+'b'+'c'+'d'+'e'+'f'+'g'+'h'+'i'+'j'+'k'+'l'+'m'+'n'+'o'+'p' - Fx: (1) == (2); in fact, if the a function containing (1) and another function containing (2) are the same other than the difference between (1) and (2), Fx treats those two functions the same function ("joined" functions), e.g. setting a property on "one" of those functions sets it on the "other" - IE: (1) >> (2) - Opera: (1) >> (2) - conclusion: Fx combines adjacent string literals into a single string literal compile-time (i.e. during parsing), while IE and Opera don't * traditional event (e.g. node.onload) and advanced event (e.g. node.addEventListener (W3C) or node.attachEvent (MS)) models do not overlap, e.g. if a listener is attached via traditional event model and another is attached via advanced event model, both listeners are executed * addEventListener cannot add the same function multiple times, while attachEvent can * IE bug (or design error)?: the this object in event listeners attached via attachEvent refers to the global object (window), not the event target * event timing: - IE: listeners added via traditional event model run first - Fx and Opera: listeners added via advanced event model run first - IE: listeners added via advanced event model run from last-added to first-added - Fx and Opera: listeners added via advanced event model run from first-added to last-added * eval is very slow - recommend: don't use eval; there's usually an alternative - uncommon cases where eval is needed: - getting value within a var, given var name in string form, in non-global scope, e.g. function foo() { var a = 10; var b = eval('a'); //if this was in global scope, window['a'] could've been used } - copying scope to a function: function foo() { alert(a); } function blah() { var a = 10; eval(foo.toString()); //creates "copy" of foo with blah's scope foo(); //this now refers to the local copy of foo, rather than the original foo } * get function/method vs. accessing var/property: (1) given: function getA() { return 10; } var r = getA(); (2) given: var a = 10; var r = a; - Fx: (2) is ~6x faster than (1) - IE: (2) is ~15x faster than (1) - Opera: (2) is ~3.5x faster than (1) - remember: calling functions takes time (function overhead) * object properties and methods are stored as a hashtable (usually): - obj[1], obj['1'], obj[1.0] are all equivalent; obj[1.3], obj['1.3'] are equivalent - keys are stored as strings - larger string keys are negligably slower - number keys are slower than string keys, since they must be converted to strings - larger number keys are significantly slower, since they must be converted to strings - size of object (number of methods and properties an object has) has no impact on performance, which is typical of a hashtable - Opera: larger objects do have a negative impact on performance, suggesting that Opera doesn't implement it as a hashtable (or at least not the same type of hashtable) * to access a property or method of an object, the object properties and methods must be searched, (size of the object shouldn't matter, since the properties and methods are stored in a hashtable) so store properties into temporary variables before using them, (storing methods can also work, but only if the method does not use the this object) but the performance increase is miniscule overall (so don't bother optimizing unless it's called a lot e.g. in inner loops) * Array vs. Object: - arrays also support string keys, since Array extends Object - only difference is that arrays have a length property and more methods - array length property is equal to the array's highest integer key - setting new properties on arrays is slightly slower than on objects (may have to do with needing to check and set the length property) * function arguments: - Fx and IE: # passed arguments have no effect on speed - Opera: # passed arguments affects speed (more => slower) - Fx and IE: # formal arguments have no effect on speed - Opera: each argument that is passed for which there is no formal argument negativel affects speed * semicolons and whitespace have no impact on performance; therefore, they are parsed when the javascript is initially parsed ("compile-time"), not during run-time * casting to string manually via toString() is much slower than automatic casting * a += b is the same speed (or negligably faster) than a = a + b (same thing applies to -=, *=, etc.) * setting a var to its own value takes as long as setting it to a different value * function expression vs. using Function constructor: - function expression: function(a0, a1, ...) {} - using Function constructor: new Function('a0,a1,...', '') - function expression is much faster than using Function constructor - function expression creates a closure, while using Function constructor does not * function expressions vs. function declaration: - functions defined by function declarations can be used before the declaration ex: foo(); function foo() {} - besides the above behavior, var foo = function() {} is equivalent to function foo() {} - IE and Fx: function expressions are barely slower than function declarations - Opera: function expressions are slightly faster than function declarations - IE: defining functions with names will define them regardless of conditionals * there is no difference in speed between a for loop and the equivalent while loop * array index function: (1) loop from first node going to next node until index is reached (2) check whether index is closer to 0 or to length; if closer to 0, loop from first node going to next node until index is reached; else, loop from last node going to previous node until index is reached - (2) is slightly faster than (1) * comparison operators: - operators > and < are slightly faster than != - comparing to 0 is usually slightly faster than comparing to other values * checking if undefined: (1) some_var === undefined (2) !some_var //only use this if some_var can have some other value that resolves to false (3) typeof somevar == 'undefined' (4) some_var == null //only use this if some_var cannot be null - 0, false, null, undefined, NaN, and '' (empty string) evaluate to false; everything else evaluates to true - Fx: (2) and (4) barely faster than (1) and (3) - IE: (2) and (4) barely faster than (1), which is faster than (3) - Opera: (2) == (4) > (1) >> (3) - note: some_var == null is equivalent to some_var == undefined; to check if some_var is really undefined, (1) or (3) must be used - note: ancient versions of Javascript don't support (1) * checking if a property exists: (1) some_obj.some_prop === undefined (2) !some_obj.some_prop //only use this if some_prop cannot have another value that resolves to false (3) typeof some_obj.some_prop == 'undefined' (4) some_obj.some_prop == null //only use this if some_prop cannot be null (5) 'some_prop' in some_obj - Fx: (2) and (4) barely faster than (1) and (3), which is faster than (5) - IE: (2) and (4) barely faster than (1), which is faster than (3) and (5) - Opera: (2) and (4) barely faster than (1) and (5), which is faster than (3) * IE bug?: defining functions with names will define them regardless of conditionals: (1) if (false) function blah() {} //preserves function name (2) if (false) blah = function() {} //doesn't preserve function name (3) if (false) blah = function blah() {} //preserves function name (4) if (false) (function() { blah = function blah() {} })() //preserves function name (5) if (false) (function() { window.blah = function blah() {} })() //preserves function name - (1) and (3) will define the function - (2) and (5) won't define the function - (4) also won't define the function, but it causes an error in IE - (4) and (5): as an added bonus, since the function is defined in an anonymous inner function, the function won't be redefined in the global scope, which is essential if the function is not defined in the global scope - note: preserving function name is usually only useful for debugging * merging arrays: (1) Array.prototype.push.apply(array1, array2); return array1; (2) Array.prototype.unshift.apply(array2, array1); return array2; (3) return array1.concat(array2) (4) manual concat: var len1 = array1.length; var len2 = array2.length; var newLen = len1 + len2; var array3 = new Array(newLen); var i = newLen - 1; for (j = len2 - 1; j >= 0; j--, i--) array3[i] = array2[j]; for (; i >= 0; i--) array3[i] = array1[i]; return array3; - (1)/(2) are faster than (3)/(4), probably because (3)/(4) have to create new arrays - (1) is faster than (2) if array1.length > array2.length - (2) is faster than (1) if array1.length < array2.length - IE, Fx: (3) is negligably faster than (4) - Opera: (3) is much faster than (4) * IE: puts ids of frames, iframes, objects, and forms into global scope, so instead of document.getElementById('some_id').tagName, some_id.tagName can be done; however, this pollutes the global scope, in that var some_id won't work and results in weird behavior * iframes: iframe element and the frame of the iframe are different: document.getElementById('iframe_id').contentWindow == frames.frame_name - IE, Fx, and Opera: supports contentWindow - Fx and Opera: supports contentDocument (== contentWindow.document) * IE bug?: if frame's onload and src (in that order) are set dynamically, frame's onload won't fire (maybe the frame's onload can fire only once, during the loading of the document) * IE: wherever the name attribute can be used, the id attribute usually works (see exception below) * IE bug?: can't use frames.iframe_name for frames dynamically created with document.createElement (works with creating them by innerHTML though); assigning a value to name doesn't make a name attribute * IE: surrounding quotes from values of attributes id and name are removed in inner/outerHTML id="id_value" => id=id_value name="name_value" => name=name_value * style.cssText: - Opera doesn't support this - setting style. will change style.cssText - two elements that are identical in style will have equivalent style.cssText, regardless of how or in what order the style was set or changed - style.cssText can be written to to set or change style * any method or property related to accessing or setting HTML/XML PCDATA (string of HTML/XML), such as innerHTML, insertAdjacentHTML, textRange.htmlText, range.createContextualFragment automatically modify the string to be well-formed HTML/XML example: suppose HTML PCDATA for body == '' (1) document.body.insertAdjacentHTML('beforeEnd', '
'); document.body.insertAdjacentHTML('beforeEnd', 'text
'); document.body.insertAdjacentHTML('BeforeEnd', '
normal
'); (2) document.body.appendChild(document.createRange().createContextualFragment('
')); document.body.appendChild(document.createRange().createContextualFragment('tex t
')); document.body.appendChild(document.createRange().createContextualFragment('
normal
')); - (1) works in IE and Opera - (2) works in Fx - IE: document.body.innerHTML == '
text\n
\n
normal
' - Fx: document.body.innerHTML == '
text
normal
' - Opera: document.body.innerHTML == '\n
text
normal
' - IE: note how IE often deletes whitespace (if any) and inserts '\n' in its place - note how Fx and Opera they don't write '
' from the second statement at all, while IE writes '
' * Fx: although I can't seem to isolate a simple test case, XMLHttpRequest.onreadystatechange is more reliable than XMLHttpRequest.onload; onload sometimes fails when requesting an xml from a servlet several times consecutively async (each request is sent after previous request's onload is triggered); in any case, onreadystatechange is faster * make sure XMLHttpRequest.onreadystatechange is set after calling XMLHttpRequest.open; otherwise, weird results * Fx: to use range.createContextualFragment, range needs to be "initialized" by arbitrarily settings its bounds; for ex, range.setStart(document.documentElement, 0); once initialized, range.createContextFragment can be used any number of times - large performance increase if Range and XMLSerializer are instantiated only once * importing nodes from a dynamically loaded document (maybe applies to any foreign document): - Fx: requires either using XMLSerializer or manual deep copy of node for node to be properly rendered importNode and cloneNode don't work - Fx: manual deep copy of node is slower than XMLSerializer if node is not a text node (in which case it's much faster) - IE: manual deep copy of node is slightly slower than setting innerHTML/getting xml if node is not a text node (in which case it's much faster) - Fx: setting innerHTML is faster than using Range/createContextualFragment and appendChild - IE: insertAdjacentHTML is very slightly faster than setting innerHTML * designMode (rich text editing mode): - activate rich test editing mode by setting doc.designMode to 'on' (case-insensitive) - IE bug: "undo/redo...seems to reset the undo buffer every time you use JavaScript to set the value of a form element or otherwise make changes to the page." - Fx bug: setting doc.designMode * input and text area value: - IE: setting value changes innerHTML and vice versa - Fx and Opera (W3C): value is independent of innerHTML (innerHTML only defines initial value); as a consequence: - Fx: window.getSelection() returns an empty selection object (toString() == '' and getRangeAt(0) returns empty range) - Opera: document.getSelection() returns empty string - both support nonstandard properties on the input/textarea element: selectionStart, selectionEnd, textLength (should be obvious what those do, and textLength is a redundant equivalent to value.length) - Fx (Gecko 1.7-) bug (to be fixed in Gecko 1.8) and Opera bug: undo buffer is reset if value property is set dynamically * Opera bug?: document.getSelection() doesn't work if you click a button, since that technically changes the selection to the button - workaround: save document.getSelection() in the document's mousedown event * Opera bug: textarea and text input .scrollTop and .scrollLeft have incorrect values, and setting them does nothing * preventing default action of tab key: - Fx: can be prevented at onkeypress - IE: can be prevented at onkeydown - Opera: cannot be prevented - workaround: create two "hidden" (position: absolute; left: 0; top: -1000px) input elements with their tabindex's properly set, one to intercept tab, other to intercept shift-tab, and when those elements are focused, they shift focus back to the original input element * IE text range: - when working in a textarea and not a designMode iframe, considers whole document as part of the textedit: expand('textedit'), moveStart/End('textedit'), etc. move the text range to include the whole document - moveStart/End('word') and other methods with similar arguments include whitespace past the word that's included up to and including the first new line characters ('\r\n'): ex: "hello []there friend" where text range is represented by the brackets; moveEnd('word', 1) will move it to: "hello [there ]friend" rather than "hello [there] friend" - text ranges that belong to different objects (such as the selection object and the textarea element) cannot be compared or used with each other in any way, even if they encompass the same text - new line chars are trimmed off ends of .htmlText and .text, and all methods that can move range by one char don't include new line chars - as a result, range.text = range.text can actually change the text! * IE design error: the args argument of the func.apply(this, args) method cannot be null, even though ECMA-262 3rd Edition specifies that a null value for this arguments is allowed; so use func.apply(this, args || []) * IE bug: when String's split method is called with a reg exp, consecutive delimiters are treated as a single delimiter - ex: 'a..b'.split(/./) => ['a', 'b'] instead of ['a', '', 'b'] - bug doesn't apply when a string delimiter is passed instead of a reg exp * Opera bug: tabs dynamically added to textarea/text input boxes sometimes aren't displayed until user modifies value in any way - workaround: convert each tab to 6 spaces * Fx bug?: dynamically setting value of textarea/text input boxes will scroll that textbox to the top and left - partial workaround: save scrollTop and scrollLeft before setting value, then restore them afterwards * use func.call method instead of func.apply method whenever possible: func.call(this, 1, 2, 3) is faster than func.apply(this, [1, 2, 3]) since the latter requires the creation of an array * getting first element by tag name: given for all: there are several previous sibling nodes of node, and there are several nodes of nodeName both inside and outside node (1) given: function getFirstChildByNodeName(node, nodeName) { for (var child = node.firstChild; child && child.nodeName != nodeName; child = child.nextSibling); return child; } getFirstChildByNodeNameCI(node, nodeName) (2) given: function getFirstChildByNodeNameCI(node, nodeName) { nodeName = nodeName.toLowerCase(); for (var child = node.firstChild; child && child.nodeName.toLowerCase() != nodeName; child = child.nextSibling); return child; } getFirstChildByNodeName(node, nodeName) //where nodeName must be capitalized if the document is HTML (3) document.getElementsByTagName(nodeName)[0] (4) node.getElementsByTagName(nodeName)[0] - there are cases where (3) and (4) won't return the correct value by their document tree traversal nature - IE and Fx: (3) and (4) are much faster than (2) and (1) - Opera: (3) and (4) are faster than (2) and (1) in most scenarios; in the best case scenario, (1) is faster than (3) and (4) - IE and Fx: (3) is faster than (4) - Opera: (4) is faster than (3) - Fx: (3) > (4) >> (1) >> (2) - IE: (3) > (4) >> (1) > (2) - Opera: (4) > (3) > (1) >> (2) - recommend: (3) if the node has no other descendant nodeName nodes besides its direct children; otherwise, (2) for HTML, and (1) for XHTML and XML * string literals and comparisons: (1) function test() { var t = 'some random string'; } (2) given: var s = 'some random string'; function test() { var t = s; } (3) given: var s1 = 'some random string'; function test() { return s1 == 'some random string'; } (4) given: var s1 = 'some random string'; var s2 = 'some random string'; function test() { return s1 == s2; } - Fx and Opera: (1) is faster than (2) - Fx: (3) is slower than (4) - IE: (2) is faster than (1) - IE and Opera: (3) is as fast as (4) - note: it would be impractical to optimize based on this, since they're all extremely fast, not to mention that it was very hard to compare results when the function call takes most of the time * string comparisons: equality vs. strict equality: (1) 'some random string' == 'some random string' (2) 'some random string' === 'some random string' - (1) is as fast as (2) (also tested inequality) * regular expression literals: (1) function test() { var t = /a/; } (2) given: var re = /a/; function test() { var t = re; } - Fx and Opera: (1) is faster than (2) - IE: (2) is much faster than (1) - note: since IE is kind of slow here, it might be reasonable to always use (2) * string searching: given for all: var s = 'some random string that is ; (semi-colon) in it'; (1) function test(div) { return s.indexOf(';'); } (2) var re1 = /;/ function test(div) { return s.search(re1); } (3) var re2 = /;/g function test(div) { return s.search(re2); } (4) function test(div) { var len = s.length; for (var i = 0; i < len; i++) if (s.charAt(i) == ';') return i; } - (1) is faster than (2) and (3) - (2) is as fast as (3) - (4) is by far the slowest * array literals: (1) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (2) new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) - Fx: (2) is slightly faster than (1) - IE, Opera: (1) is slightly faster than (2) * closures: run: function foo() { var x = 10; window.foo1 = function() { alert(x); } x = 5; window.foo2 = function() { alert(x); } } var foo1, foo2; foo(); foo1(); foo2(); output: 5 5 conclusion: variables in scope are not copied for each inner function instead, each inner function has a reference to function scope therefore, the size of the scope (number of vars defined) does not affect the speed of defining inner functions and inner functions "share" the variables in scope * new Function: functions created via the Function constructor are not closures * Opera bug: creating an anonymous function without assigning it to a var or passing it into a function throws an error ex: function() {} //not alert(function() {}) * Object.prototype.constructor: run: function classA() {} function classB() {} classB.prototype = new classA(); var objB = new classB(); alert(objB.constructor); output: function classA() { } conclusion: constructor property refers to the base of the inheritance tree * element (as in list element, not DOM element) addition and removal in native arrays (new Array()) are much faster than custom linked lists except for very large lists (> 100 in Firefox, even more in IE) - garbage collector also has a hard time handling a lot of deletions * IE: if a rule is changed on a stylesheet right after an alert, the page isn't automatically re-rendered (resizing will cause it re-render) * attempting to return an object in a constructor will cause that object to be returned instead of the object being constructed ex: function XMLHTTP() { return XMLHttpRequest(); } var xmlhttp = new XMLHTTP(); //xmlhttp is an object of type XMLHttpRequest instead of type XMLHTTP * labelled statements and break statements: - any statement can have a label; ex: label: for(...) { ... } - break and continue statements can target labels that nest those break/continue statements; ex: label: for(...) { for (...) { if (...) break label; } } - break statement cannot be used as a goto statement (can only target labels that nest them) - this can be used in conjunction with the block statement; ex: label: { if (...) break label; ... } * block statements, i.e. { ... } - unlike most other C-style languages, block statements does not introduce a new scope * IE: events belong to windows - that means that an event in another frame are stored in the frame's event object (frame.contentWindow.event) rather than window.event * Math.sin and Math.cos: given for all: var sin = Math.sin; var PI = Math.PI; var RAD_TO_DEG = 180 / PI; (1) sin(PI) (2) given: var sinTable = {}; for (var t = 0, i = 0, ts = PI / 180; i < 360; t += ts, i++) sinTable[t] = sin(t); sinTable[PI] (3) given: var sinTable = {}; for (var t = 0, i = 0, ts = PI / 180; i < 360; t += ts, i++) sinTable[i] = sin(t); sinTable[PI * RAD_TO_DEG]; - only (1) supports angles <>= 2 * Math.PI - (3) ~> (1) > (2) - (2) is slow probably because of the floating point keys (number keys must be converted to string keys) - same thing applies to Math.cos * redeclaring variables in loops: (1) for (var i = 0; i < 1000; i++) { var x = i; } (2) var x; for (var i = 0; i < 1000; i++) { x = i; } - Fx and Opera: (1) == (2) - IE: (2) ~> (1) * childNodes and getElementsByTagName vs. manual node arrays: (1) var array = hiddendiv.childNodes; (2) var array = hiddendiv.getElementsByTagName('*'); (3) var array = new Array(hiddendiv.childNodes.length); for (var node = hiddendiv.firstChild, i = 0; node; node = node.nextSibling, i++) array[i] = node; (4) given: (1) array[i]; (5) given: (2) array[i]; (6) given: (3) array[i]; - note on (2): this will only work if children of hiddendiv do not contain any children themselves - note: childNodes and getElementsByTagName do not actually return arrays; they return HTMLCollection objects - Fx and IE: (1) > (2) >>> (3) - Opera: (1) >> (2) >>> (3) - Fx: (6) >> (4) > (5) - IE: (6) >> (5) > (4) - Opera: (6) > (4) == (5) - conclusion: childNodes and getElementsByTagName is faster in the short run, but slower in the long run (when the array is accessed a lot) - Fx and Opera: (1) * ~30 (4) == (3) * ~30 (6) means that (3) is overall faster than (1) when array is accessed ~30 times - IE: (1) * ~15 (4) == (3) * ~15 (6) means that (3) is overall faster than (1) when array is accessed ~15 times

javascript lib

http://www.howtocreate.co.uk/jslibs/ http://www.mattkruse.com/javascript/dragiframe/index.html http://cross-browser.com

Accessing the document inside an iframe

Browser Disagreements

contentWindow vs. contentDocument

  • IE (Win) and Mozilla (1.7) will return the window object inside the iframe with oIFrame.contentWindow.
  • Safari (1.2.4) doesn't understand that property, but does have oIframe.contentDocument, which points to the document object inside the iframe.
  • To make it even more complicated, Opera 7 uses oIframe.contentDocument, but it points to the window object of the iframe.

Because Safari has no way to directly access the window object of an iframe element via standard DOM (or does it?), our fully modern-cross-browser-compatible code will only be able to access the document within the iframe. The resulting code follows.

http://xkr.us/articles/dom/iframe-document/

Thursday, August 04, 2005

JS树-类库