Move this topic
addDefault
Implemented
- Maybe later
- Under review
- In-progress
- Implemented
- Will not implement
in Developing jQuery Core
•
11 years ago
After talking w/ Yehuda, we came up with the idea of allowing people to provide default actions on triggered events.
I discuss it a little on JMVC's forum.
http://groups.google.com/group/javascriptmvc/browse_thread/thread/4d5ae9c61f3817f0
But here's what it looks like essentially:
//imagine some plugin wanting to provide default functionality on a "hide" event.
$(".tab").bind("hide", function(ev){ And allow you to do something like: $(".tab").bind("hide", function(ev){
var el = this;
ev.addDefault(function(){
$(el).hide()
})
})
if(!CURRENT_TAB_COMPLETE) ev.preventDefault(); }) Similar to how the browser works. This might have problems if people expect defaults to be added after a stopPropagation is called. But, I think the idea is powerful. Here's a working version:I'm going to make a plugin for this, but I think it could be a very useful addition to core, or at least something jQuery UI takes a look at.
1
Replies(15)
Re: addDefault
11 years ago
Justin, you can already do this in jQuery 1.4 by using jQuery.event.special:
$.event.special.hide = {
add: function(handler){
return function(e){
handler.apply( this, arguments );
if ( !e.isDefaultPrevented() ) {
$(this).hide();
}
};
}
};
var elem = $('a:first'),
prevent = true;
elem.bind('hide', function(e){
alert( 'hide' );
prevent && e.preventDefault();
});
elem.trigger( 'hide' ); // 'hide' is alerted, elem is not hidden.
prevent = false;
elem.trigger( 'hide' ); // 'hide' is alerted, elem is hidden.
add: function(handler){
return function(e){
handler.apply( this, arguments );
if ( !e.isDefaultPrevented() ) {
$(this).hide();
}
};
}
};
var elem = $('a:first'),
prevent = true;
elem.bind('hide', function(e){
alert( 'hide' );
prevent && e.preventDefault();
});
elem.trigger( 'hide' ); // 'hide' is alerted, elem is not hidden.
prevent = false;
elem.trigger( 'hide' ); // 'hide' is alerted, elem is hidden.
- Ben
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, Star Wipe, Untils, unwrap, urlInternal.. and more!
Leave a comment on cowboy.ben's reply
Re: addDefault
11 years ago
Ah ... didn't log in before replying ... so I will write this again ....
Ben, great idea for an alternative method, but it's not exactly the same thing for 2 reasons:
1, Using special only elegantly allows for a single default action for that event. Default actions are typically context aware. A click does something different for a link vs an input, vs a submit button in a form. User should be able to associate the default action with whatever plugin's api they are trying to expose.
2. I think using special would still trigger the default event even if a parent element called preventDefault. The method I used allows for parent elements to prevent the default event, similar to how it works in the DOM.
Leave a comment on justinbmeyer's reply
Re: addDefault
11 years ago
Having multiple default callbacks bound to any one event is dangerous, because how do you know which one can cancel the other?
Also, I don't like the way, in your initial example, a callback is bound to the 'hide' event on '.tab' just to set the default callback for that event for that selector's elements. It feels awkward, because once the default action is set, of what use is the callback in which the default action is bound?
Either way, it might be more flexible to just allow an optional "priority" flag. The flag could be true "always fire this event first" (allowing it to cancel other events) or false "always fire this event last" (allowing it to be canceled by other events) and if it's undefined, behave normally.
// Always execute this callback first.
$(".tab").bind("hide", function(e){
if ( some_condition ) {
e.preventDefault();
}
}, true);
// Always execute this callback last.
$(".tab").bind("hide", function(e){
$(this).hide()
}, false);
$(".tab").bind("hide", function(e){
if ( some_condition ) {
e.preventDefault();
}
}, true);
// Always execute this callback last.
$(".tab").bind("hide", function(e){
$(this).hide()
}, false);
- Ben
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, Star Wipe, Untils, unwrap, urlInternal.. and more!
Leave a comment on cowboy.ben's reply
Re: addDefault
11 years ago
Ben,
I'm not sure what you mean by which one can cancel the other ... any preventDefault will cancel all of them. Similar to how canceling a click, might cancel various different default acitons.
Being accustomed to multiple event handlers on the same event type, and hopefully familiar with bubbling,
I think this is something most people will be used to.
I agree that it is awkward. Your example feels much more natural. However, I worry that people will think false is for bubble / capture. I wonder how I can signal default. Maybe
- $(".tab").bind("default.hide", function(e){$(this).hide()})
Leave a comment on Guest's reply
Re: addDefault
11 years ago
What about preventing just a particular name-spaced event handler?
This can be accomplished by extending the jquery event object prototype. In order for this to work, jQuery must be manually bubbling the event, so there is a little trickery with canceling and then manually triggering a new event.
The "jQuery.event.handle" function also needs a slight tweak on line 1838 (jQuery 1.4):
I put together a quick demo of this functionality, I think it has real merit...
http://jsbin.com/oloho
- $('.tab').bind("click.tab-default', handler );
- // then in a descendant "click" handler...
- event.suppress("tab-default");
This can be accomplished by extending the jquery event object prototype. In order for this to work, jQuery must be manually bubbling the event, so there is a little trickery with canceling and then manually triggering a new event.
- jQuery.Event.prototype.suppress = function( ns ){
- // if never suppressed force a manual bubbling situation
- if ( !this.suppressed ){
- // create a new event to trigger
- var event = new jQuery.Event( this );
- event.suppressed = {};
- event.suppressed[ ns ] = true;
- // continue the bubbling with a new event
- jQuery( this.target ).trigger( event );
- // abandon current handling...
- this.stopImmediatePropagation();
- }
- else { // remember namespace
- this.suppressed[ ns ] = true;
- }
- };
- jQuery.Event.prototype.isSuppressed = function( ns ){
- return !!( this.suppressed || {} )[ ns ];
- };
The "jQuery.event.handle" function also needs a slight tweak on line 1838 (jQuery 1.4):
- if ( !event.isSuppressed( handler.type ) && ( all || namespace.test( handler.type ) ) ) {
I put together a quick demo of this functionality, I think it has real merit...
http://jsbin.com/oloho
Leave a comment on mike.helgeson's reply
Re: addDefault
11 years ago
I think this sounds like a reasonable addition - and not particularly hard, either. I'll check in to it.
Re: Re: addDefault
11 years ago
I added in the ability to have a special default event the other day:
http://dev.jquery.com/ticket/5973
I don't think it makes much sense (right now) to add in multiple default handlers, considering that even single default handlers don't exist, and aren't used, yet. No reason to land complicated code for a hypothetical situation.
I don't think it makes much sense (right now) to add in multiple default handlers, considering that even single default handlers don't exist, and aren't used, yet. No reason to land complicated code for a hypothetical situation.
Leave a comment on jeresig's reply
Re: addDefault
11 years ago
It would be great if it also worked with live. Consider how nice it would be to do something like:
$(".tab").addLiveDefault("click",function(){ ... });
Which goes a long way of making every ".tab" act like a native browser widget.
Leave a comment on justinbmeyer's reply
Re: addDefault
11 years ago
+1 for both addDefault() and liveAddDefault().
In fact, its more reasonable in live, since it would feel more like a default brower event, since it doesn't meter if you create the element after, the default is aways there.
In fact, its more reasonable in live, since it would feel more like a default brower event, since it doesn't meter if you create the element after, the default is aways there.
Leave a comment on irae.jquery's reply
Re: addDefault
11 years ago
Understandable. It exists now in good "at-one-point-hypothetical" company like Space-Time, an African-American U.S. President, and namespaced events
.
Instead of measuring the 1919 eclipse, I'll prove the idea by releasing it as a plugin.
It will likely use namepaced events like:
$(".foo").bind("default.click", funciton(){});
$(".tab").live("default.click", funciton(){});
Leave a comment on justinbmeyer's reply
Re: addDefault
11 years ago
I just posted an example of how this kind of thing can be done in jQuery 1.4.2, now that the special event _default method has been added: http://jsfiddle.net/kVGpa/
- Ben
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, resize event, Star Wipe, Untils, unwrap, urlInternal.. and more!
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, resize event, Star Wipe, Untils, unwrap, urlInternal.. and more!
Leave a comment on cowboy.ben's reply
Re: addDefault
11 years ago
Ben,
Very cool, but what type of 'thing'? I'm guessing you mean default events in general, not the problem I was discussing.
If you did mean the problem of having 'remove' on multiple widgets, this works if you label each widget with '.widget'. But, there can still only be 1 default remove event for everything, which I don't think will be useful for complex apps.
Leave a comment on justinbmeyer's reply
Re: addDefault
11 years ago
Justin, why not something like:
$.event.special.hide = {
_default: function( event ) {
var target = $(event.target);
if ( target.is( '.tab' ) ) {
target.hide();
} else if ( other_condition ) {
target.other_method();
}
}
};
_default: function( event ) {
var target = $(event.target);
if ( target.is( '.tab' ) ) {
target.hide();
} else if ( other_condition ) {
target.other_method();
}
}
};
One benefit to this approach is that, since you're not binding the default behavior to specific elements, but are instead testing that event.target meets certain predefined criteria at the time of triggering, as new elements meeting those criteria are added into the DOM, they will continue to behave as expected.
Consider how the browser works. When you click a submit button, the default behavior is different than that of a div or a link. No matter how many new submit buttons, divs or links are added to the page, the default behavior stays consistent, with no action required after adding those elements to "re-enable" this default behavior. It's predefined.
If you need a more modular approach, you could maintain an array of test callbacks to run sequentially, providing test add/remove/reorder utility methods, as needed:
var tests = [
function( event, target ) {
if ( target.is( '.tab' ) ) {
target.hide();
}
},
function( event, target ) {
if ( other_condition ) {
target.other_method();
}
}
];
$.event.special.hide = {
var target = $(event.target);
_default: function( event ) {
$.each( tests, function(i,test){
return test( event, target );
});
}
};
function( event, target ) {
if ( target.is( '.tab' ) ) {
target.hide();
}
},
function( event, target ) {
if ( other_condition ) {
target.other_method();
}
}
];
$.event.special.hide = {
var target = $(event.target);
_default: function( event ) {
$.each( tests, function(i,test){
return test( event, target );
});
}
};
- Ben
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, resize event, Star Wipe, Untils, unwrap, urlInternal.. and more!
Leave a comment on cowboy.ben's reply
Re: addDefault
11 years ago
Ben,
Thanks for your thoughts! Please excuse my passion on this topic. I have fallen in love with event based APIs, and default events are central to make it a workable architecture.
I am thinking about how the browser works, but implementing the idea through event delegation. In my head, it's much easier to just do:
- $(".tab").live("default.hide", function(){})
Maybe it wasn't clear that I was intending this for live (which is why I submitted a patch to enable live to work with namespaces).
However, I don't really buy into the 'jquery must match the browser' argument. It should be close; however, the default event system of browsers wasn't designed with scalability in mind. I don't think they expected people to have their own default events.
The browser doesn't have any complex widgets. But if it did, I think they would want the default behavior of the complex widget by the widget code. I want to organize how my widget responds to events. If a tab responds to "open", I shouldn't have to dramatically change how I get it to respond to "default.open". In my opinion, default events should be a central piece to building jQuery widgets. Any extra unnecessary difficulty to make that happen doesn't seem worth it.
The first method won't won't scale b/c other plugins can stomp on $.event.special.hide._default
Your second would scale if this was the WAY jquery made defaults work, meaning instead of _defaults, you can push various default methods.
I'd be happier with that, but it's still not as pretty as live.
Leave a comment on justinbmeyer's reply
Re: addDefault
11 years ago
Justin, everything you're saying makes sense. It looks like you're asking for less of a plugin-specific special event default behavior, which is what _default provides, and more of an implementation-specific application default behavior, or set of behaviors.
I like your last syntax suggestion, which is quite unobtrusive:
$(".tab").live("default.hide", function(e){ $(event.target).hide(); });
But what about this syntax, where a special "default" namespace is reserved for adding/removed default behaviors, which when combined with other namespaces could provide namespace-specific defaults:
$(".tab").live("hide.default", function(e){ $(event.target).hide(); });
$(".tab").live("hide.default.foo.bar", function(e){ $(event.target).hide(); });
Of course, managing these multiple default behaviors could get pretty complicated. Still, I like the idea of an API around managing multiple default event behaviors using the existing $.fn.live syntax.
- Ben
http://benalman.com/ - jQuery BBQ, doTimeout, hashchange event, Message Queuing, postMessage, replaceText, resize event, Star Wipe, Untils, unwrap, urlInternal.. and more!
Leave a comment on cowboy.ben's reply
Change topic type
Link this topic
Provide the permalink of a topic that is related to this topic
Reply to justinbmeyer's idea
{"z165885":[14737000000658450,14737000000661131,14737000000668550,14737000000676197,14737000000708160,14737000000708178],"z2566198":[14737000000669311,14737000000674494],"z2951053":[14737000000660895,14737000000663052,14737000000709043,14737000000708176,14737000000708271],"z2950936":[14737000000671755],"z2878318":[14737000000667073],"z-1":[14737000000665055]}