[Jifty-commit] r1545 - jifty/trunk/share/web/static/js

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Wed Jul 12 12:00:35 EDT 2006


Author: nelhage
Date: Wed Jul 12 12:00:11 2006
New Revision: 1545

Modified:
   jifty/trunk/share/web/static/js/app_behaviour.js

Log:
Documenting how to write sane Behaviours that don't leak memory (leak
less memory, probably) in IE and aren't dog-slow.

Modified: jifty/trunk/share/web/static/js/app_behaviour.js
==============================================================================
--- jifty/trunk/share/web/static/js/app_behaviour.js	(original)
+++ jifty/trunk/share/web/static/js/app_behaviour.js	Wed Jul 12 12:00:11 2006
@@ -1,29 +1,116 @@
 /*
- * Doc below taken from http://ripcord.co.nz/behaviour/
- *
-IMPORTANT: if you make DOM changes that mean that an element
-ought to gain or lose a behaviour, call Behaviour.apply()!
-
-(Actually, that *won't* make something lose a behaviour, so if that's necessary
-you'll need to have an empty "fallback".  I.E. If "div#foo a" should have a
-special onclick and other "a" shouldn't, then there ought to be an explicit "a"
-style that sets onclick to a trivial function, if DOM changes will ever happen.)
-(Also, with the current behaviour.js, the order of application of styles is
-undefined, so you can't really do cascading.  I've suggested to the author
-that he change it; if he doesn't, but we need it, it's an easy change to make
-the sheets arrays instead of Objects (hashes).  For now this can be dealt with
-by loading multiple sheets (register calls), though.)
-*/
 
-/* Here's an example... */
+  This file is intended for you to add application-specific Javascript
+  behaviours. See http://bennolan.com/behaviour/ for an introduction
+  to the Behaviour library.
 
+  Behaviour lets you apply javascript to elements of the DOM using CSS
+  selectors. A simple example:
 
-/*
-var myrules = {
-    "h2.rounded": function(element) {
-        Rico.Corner.round(element);
+  var myrules = {
+      "h2.rounded": function(element) {
+  	Rico.Corner.round(element);
+      }
+  };
+          
+  Behaviour.register(myrules);
+
+  In general, you'll rarely if ever have to worry about calling
+  Behaviour.apply() yourself -- Jifty will take care of it on DOM load
+  and on any AJAX updates that it does.
+
+
+  Some Notes About Writing Behaviours
+  ===================================
+
+  * Jifty's Behaviour.js uses the cssQuery[1] library to do our DOM
+    lookups by CSS selector. cssQuery is very powerful, but can be
+    slow as DOM size grows. For best performance, follow these
+    guidelines when writing behaviours, whenever possible:
+
+    * Prefer selectors that begin with '#id'. cssQuery will use
+      document.getElementByID to get the ID in question, meaning we
+      only have to search a small fragment of the DOM by hand
+
+    * Barring that, prefer selectors of the form 'element.class' over
+      simply '.class' selectors. This lets us filter for that element
+      specifically using DOM calls, again hugely reducing the amount
+      of DOM walking we have to do. 
+
+    [1] http://dean.edwards.name/my/cssQuery/
+
+
+  * Behaviour has something of a reputation for leaking memory. The
+    reason for this is a common idiom used in constructing
+    behaviours. Code like:
+
+    Behaviour.register({
+        'a.help': function(e) {
+            e.onclick = function() {
+        	openInHelpWindow(this);
+        	return false;
+            }
+        }
+    });
+
+    will leak memory in Internet Explorer, thanks to how IE handles
+    garbage collection (See the footnote for details). To avoid this,
+    you can use one of the following two idioms:
+
+    (a) declare the onclick function elsewhere:
+
+    function openHelpLink() {
+        openInHelpWindow(this);
+        return false;
     }
-};
-        
-Behaviour.register(myrules);
+    
+    Behaviour.register({
+        'a.help': function(e) {
+            e.onclick = openHelpLink;
+        }
+    });
+
+    (b) Set the element to 'null' at the end of the Behaviour function:
+
+    Behaviour.register({
+        'a.help': function(e) {
+            e.onclick = function() {
+        	openInHelpWindow(this);
+        	return false;
+            }
+	    e = null;
+        }
+    });
+
+
+    ** Footnote **
+
+    The reason that code leaks in IE is that Internet Explorer uses
+    reference counting to manage memory in its Javascript engine,
+    which means that circular references are never freed. When you
+    write this code:
+
+    Behaviour.register({
+        'a.help': function(e) {		// <-- FUNCTION A
+            e.onclick = function() {	// <-- FUNCTION B 
+        	openInHelpWindow(this);
+        	return false;
+            }
+        }
+    });
+
+    You are in fact creating a circular data structure because
+    function `B', when it is created, stores a reference to the
+    environment in which it was created, which includes the variable
+    `e'. `e', however, also references function `B' through its
+    `onclick' property, and this a circular chain of references is
+    created, which IE will never garbage collect.
+
+    Solution (a) addresses this by moving function `b' outside of the
+    scope where `e' is defined. Solution (b) addresses it by setting
+    `e' to null in the environment around `b', which means that that
+    environment no longer contains a reference to that DOM node, and
+    the loop no longer exists.
+
 */
+


More information about the Jifty-commit mailing list