Binds Class Mutator

Aaron Newton has made a new version of the Binds mutator that's used just like the one I've shown here, but works with MooTools 1.2.

When writing a Class you often want to bind a class method to an event and access this as the instance of the class. You could say this isn't a big deal using .bind(). That's right until it comes to removing the event again, since Function::bind works in that way that it returns a new function wrapping the old one. Hence you need to store that very function when you want to remove it again (without removing all events from that element, which isn't good inside a portable class).

What I've seen a couple of times is an object holding all bound functions and created in the constructor, example:

  1. var MyClass = new Class({
  2.  
  3. initialize: function(){
  4. this.bound = {
  5. sayHello: this.sayHello.bind(this),
  6. sayGoodbye: this.sayGoodbye.bind(this)
  7. }
  8. // now only using this.bound.sayHello and
  9. // this.bound.sayGoodbye inside the class
  10. },
  11.  
  12. sayHello: function() { /* ... */ },
  13.  
  14. sayGoodbye: function() { /* ... */ }
  15.  
  16. });
I wasn't quite happy with this solution, because it's too verbose: why always use this.bound.myFn when I always want the bound one? Secondly, I don't want to do the binding of all these functions by hand. After some discussions on different solutions this is what I come up with as the optimum between speed (don't worry its faster than the solution above) and usability.

A new so called "class mutator" named Binds. Most of you probably didn't hear of class mutators before, but you sure have used a class mutator before when you've written one or another class. Built in mutators are Implements and Extends and here is the code of the Binds mutator:
  1. Class.Mutators.Binds = function(self, methods) {
  2.  
  3. $splat(methods).each(function(method){
  4. var fn = self[method];
  5. self[method] = function(){
  6. return fn.apply(self, arguments);
  7. };
  8. });
  9.  
  10. };
What it does is overriding all methods passed to it as a string with a version bound to the instance. Binds: 'foo' would just bind foo to the class, Binds: ['foo', 'bar'] binds foo and bar. Pretty simple, huh?

Now let's see how the class can be simplified using the Binds mutator:
  1. var MyClass = new Class({
  2.  
  3. Binds: ['sayHello', 'sayGoodbye'],
  4.  
  5. initialize: function(){
  6. // now only using this.sayHello and
  7. // this.sayGoodbye inside the class
  8. },
  9.  
  10. sayHello: function() { /* ... */ },
  11.  
  12. sayGoodbye: function() { /* ... */ }
  13.  
  14. });

I hope you learned a bit again or can find use of it.

If you have any wishes or ideas for further articles let me know.

outerClick event

Someone in the #mootoos irc channel wanted an event triggered only when clicked outside of an element but there's nothing built-in, so I thought that it's an interesting event to implement. And since it has at least some reusability I'm going to share the code along with a description of what it's doing.

  1. (function(){
  2. var events;
  3. var check = function(e){
  4. var target = $(e.target);
  5. var parents = target.getParents();
  6. events.each(function(item){
  7. var element = item.element;
  8. if (element != target && !parents.contains(element))
  9. item.fn.call(element, e);
  10. });
  11. };
  12. Element.Events.outerClick = {
  13. onAdd: function(fn){
  14. if(!events) {
  15. document.addEvent('click', check);
  16. events = [];
  17. }
  18. events.push({element: this, fn: fn});
  19. },
  20. onRemove: function(fn){
  21. events = events.filter(function(item){
  22. return item.element != this || item.fn != fn;
  23. }, this);
  24. if (!events.length) {
  25. document.removeEvent('click', check);
  26. events = null;
  27. }
  28. }
  29. };
  30. })();
  31.  
  32. // usage
  33. window.addEvent('domready', function(){
  34. $('test').addEvent('outerClick', function(){
  35. alert('You clicked outside of $("test").');
  36. });
  37. });
  38.  

First thing you may notice is the (function(){ /* code here */ })() pattern. That creates a private area for the events variable, which will store all the added outerClick events and the check function. This way the global namespace is not polluted.

Now let's take a look at onAdd. It checks if the events variable is set, and if not it initializes it with an empty array and adds the check function as a document click event listener. Note that this is only done once for all outerClick events added. The element and the user supplied event listener fn are pushed to the events array as the last step of onAdd.

onRemove does the same things onAdd does but in reverse. It filters the element with the function and element out of the events array and if the array is empty it removes the document click listener.

Last part of the code to discuss is the check function. The idea behind it is to check if the event target is outside the the element in events and if so it calls the event function. The target is 'inside' the element if its the element itself or if the element is one of the targets parents. In order to do the search for the parents only once per click target.getParents() is saved to a temporary variable.

The post is now way longer than expected, though I hope you've learned a bit from my first serious post.

Welcome

Welcome to my brand new blog, I'm going to publish random mootools snippets here.


About

I'm a MooTools core developer and this is the place where I publish some snippets and other mootools related stuff. I study computer science in Darmstadt, Germany.

Idea for a Topic?

If you've got an idea for a topic to write about, let me know!

Other Blogs

User