jQuery: Extending bind() to define context of execution (scope)

One of the main advantages of jQuery is that it does a lot of thinking for you, especially with more complex things like event handling. Unfortunately, as you move into more advanced application building, the jQuery developer is left without a standard method of applying context to an event.

Although creating a generic bind function to use the Function object’s call or apply methods is always an option, it suffers from two problems:

  • Not integrated with the library, which can cause developer confusion and creates hard to read code
  • Unbinding that handler becomes problematic without additional bookkeeping

While many have called for scope of execution to be added to bind, you can simply extend the functionality yourself with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
function() {
    jQuery.extend(jQuery.event, {
        add: function(elem, types, handler, data, scope) {
            if ( elem.nodeType == 3 || elem.nodeType == 8 )
                return;
 
            // For whatever reason, IE has trouble passing the window object
            // around, causing it to be cloned in the process
            if ( elem.setInterval && elem != window )
                elem = window;
 
            // Make sure that the function being executed has a unique ID
            if ( !handler.guid )
                handler.guid = this.guid++;
 
            // if data or scope is defined, use a proxy function
            if( data !== undefined || scope !== undefined) {
                // Create temporary function pointer to original handler
                var fn = handler;
 
                // Create unique handler function, wrapped around original handler
                if ( scope !== undefined) {
                    // Create proxy function to apply correct context determined by scope
                    var proxyFn = function(){ return fn.apply(scope, arguments); };
                    handler = this.proxy( fn, proxyFn );
                } else {
                    handler = this.proxy( fn );
                }
            }
 
            // if data is passed, bind to handler
            if ( data !== undefined ) {
                // Store data in unique handler
                handler.data = data;
            }
 
            // Init the element's event structure
            var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
                handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
                    // Handle the second event of a trigger and when
                    // an event is called after a page has unloaded
                    return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                        jQuery.event.handle.apply(arguments.callee.elem, arguments) :
                        undefined;
                });
            // Add elem as a property of the handle function
            // This is to prevent a memory leak with non-native
            // event in IE.
            handle.elem = elem;
 
            // Handle multiple events separated by a space
            // jQuery(...).bind("mouseover mouseout", fn);
            jQuery.each(types.split(/\s+/), function(index, type) {
                // Namespaced event handlers
                var namespaces = type.split(".");
                type = namespaces.shift();
                handler.type = namespaces.slice().sort().join(".");
 
                // Get the current list of functions bound to this event
                var handlers = events[type];
 
                if ( jQuery.event.specialAll[type] )
                    jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
 
                // Init the event handler queue
                if (!handlers) {
                    handlers = events[type] = {};
 
                    // Check for a special event handler
                    // Only use addEventListener/attachEvent if the special
                    // events handler returns false
                    if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
                        // Bind the global event handler to the element
                        if (elem.addEventListener)
                            elem.addEventListener(type, handle, false);
                        else if (elem.attachEvent)
                            elem.attachEvent("on" + type, handle);
                    }
                }
 
                // Add the function to the element's handler list
                handlers[handler.guid] = handler;
 
                // Keep track of which events have been used, for global triggering
                jQuery.event.global[type] = true;
            });
 
            // Nullify elem to prevent memory leaks in IE
            elem = null;
        }
    });
 
    jQuery.fn.extend({
        bind: function( type, data, fn, scope ) {
            return type == "unload" ? this.one(type, data, fn, fn && scope) : this.each(function(){
                jQuery.event.add( this, type, fn || data, fn && data, (fn && data) && scope );
            });
        }
    });
}();

As you can see, you simply use bind as you always have, but, if you wish to define scope, pass the context in as the last parameter. You ARE required to pass something for data with this method. If you have nothing to pass, simply send an empty object: {}. The bookkeeping is done by jQuery, and you should be able to unbind without any headaches.

Please note: This isn’t fully tested, but it’s based off changes I made elsewhere. I will update this later if I find issues with it.

19 Responses to “jQuery: Extending bind() to define context of execution (scope)”

  1. gregory Says:

    Very cool extension! I have been wondering why similar functionality isn’t built into jQuery core code. Scoping for objects is part of mootools and has been what I miss the most in my switch from mootools to jquery. I am getting sick of adding var self = this; when I need to refer to my object.

    p.s. your code contains & not & 🙂

  2. Brian Lacy Says:

    Can anyone clarify for me WHY this functionality is not included in jQuery already? Binding event handlers and parameters to a particular scope is a royal pain in jQuery, and its something I have to do quite frequently.

  3. Viraj Says:

    @Brian, from what I’ve heard/read, its simply unnecessary within the jQuery paradigm. And this makes sense: all jQuery methods are called on an array of selected elements, and ‘this’, in this context, should reference the current element being manipulated.

    That being said, there’s no reason this shouldn’t be in the library. It’s something that makes doing complex OOP with jQuery more difficult than it needs to be. There are plenty of others who’ve also expressed interest in seeing this, but apparently not enough…

    @gregory, sorry for the crappy formatting, I need to find a new wordpress plugin…

  4. adam j. sontag Says:

    this is going to be in the library as of jquery 1.3.3
    http://brandonaaron.net/blog/2009/05/12/jquery-edge-bind-with-a-different-this#comments

  5. Viraj Says:

    Just an update, this was included in 1.3.3. Described here with other updates: http://www.slideshare.net/jeresig/recent-changes-to-jquerys-internals

  6. Proxy Seite Says:

    Proxy Seite…

    […]jQuery: Extending bind() to define context of execution (scope) | sanghvi labs[…]…

  7. dobra veza Says:

    You’re truly a good webmaster. The website loading pace is amazing. It sort of feels that you’re doing any distinctive trick. Furthermore, The contents are masterwork. you have performed a great activity in this topic!

  8. glu mobile games Says:

    Howdy! Quick question that’s entirely off topic.
    Do you know how to make your site mobile friendly?
    My site looks weird when viewing from my iphone 4. I’m trying to find a template or plugin that might be able to correct
    this issue. If you have any recommendations, please share.
    Many thanks!

  9. Newton Says:

    WOW just what I was looking for. Came here by searching for applying context

    Stop by my homepage – telecharger pdf creator – Newton,

  10. real racing 3 hack tool free download Says:

    Thank you for the good writeup. It in truth was a amusement account it.
    Look complex to more introduced agreeable from you!
    By the way, how could we be in contact?

    Here is my blog post: real racing 3 hack tool free download

  11. Berry Says:

    It’s hard to find your articles in google. I found it on 15 spot, you should build quality backlinks
    , it will help you to rank to google top 10.
    I know how to help you, just type in google – k2 seo tricks

  12. sniper elite 3 telecharger Says:

    My family members always say that I am killing my time here at web, except I know I am getting experience every day by reading thes good posts.

  13. Emmett Says:

    I read a lot of interesting articles here. Probably you spend a lot of time writing, i know how to
    save you a lot of work, there is an online tool that creates readable, SEO friendly articles in seconds, just type in google – laranitas free content source

  14. Sudheesh Says:

    I have seen plenty of uefsul items on your web page about personal computers. However, I have got the viewpoint that notebooks are still more or less not powerful sufficiently to be a good option if you typically do tasks that require lots of power, ju

  15. www.wordpress.com Says:

    Good day very nice site!! Guy .. Beautiful ..
    Wonderful .. I will bookmark your blog and take the feeds additionally?
    I am happy to search out numerous helpful
    info right here within the publish, we want develop extra strategies
    on this regard, thanks for sharing. . . . . .

  16. cheap madden coins Says:

    Great internet websitewebsite! It looks really good! Maintain the helpful job!

  17. %url% Says:

    I delight in the data on your website. Many thanks!.

  18. Hospitality supplies first Says:

    Hospitality supplies first

    jQuery: Extending bind() to define context of execution (scope) | sanghvi labs

  19. Digital Marketers Says:

    Digital Marketers

    jQuery: Extending bind() to define context of execution (scope) | sanghvi labs

Leave a Reply