I know how to bind multiple events and all that stuff. What I want to do is have multiple events occur to trigger a function.
Like
$(this).click and $(this).mousemove triggers a function
Is there a way to do this? Is it possible or am I just dreaming.
With a better understanding now, one thing you could do is have one event bind and unbind the other:
Example: http://jsfiddle.net/ZMeUv/
$(myselector).mousedown( function() {
$(document).mousemove(function() {
// do something
});
$(document).mouseup(function() {
$(this).unbind(); // unbind events from document
});
});
This prevents the mousemove from constantly firing when you have no need for it.
You can use jQuery's special events to package everything nicely and optimize things in the process. A mousedown and mousemove combo also commonly goes by the name "drag", so here's an example of creating a drag event that you can bind to elements. Note, that this code is specific to jQuery 1.4.2
One of the advantages to using this is that you only bind the mousemove, mouseout, and mousedown handlers once each element, no matter how many times that element is bound to the drag event. Now this isn't the most optimal way of doing it, and you can setup just 3 handlers on the document and manage everything with it, which is equally easy to do with the special events API. It just provides a nicely packaged way of building complex interactions than would be possible with just native events or custom events, in the jQuery sense.
$("..").bind("drag", function() {
...
});
I will try and add more documentation on what's actually going on, as it looks pretty unintuitive, I must confess. Checkout another nice article on the topic.
See an example of this here. To create this custom special event, use:
jQuery.event.special.drag = {
// invoked each time we bind drag to an element
add: function(obj) {
var originalHandler = obj.handler;
obj.handler = function(event) {
var el = jQuery(this);
if(el.data('mousePressed')) {
return originalHandler.apply(this, arguments);
}
};
},
// invoked only the first time drag is bound per element
setup: function(data, namespaces) {
var el = jQuery(this);
el.data('mousePressed', false);
el.bind('mousedown', function() {
jQuery(this).data('mousePressed', true);
});
jQuery(document).bind('mouseup', function() {
el.data('mousePressed', false);
});
el.bind('mousemove', jQuery.event.special.drag.handler);
},
// invoked when all drag events are removed from element
teardown: function(namespaces) {
var el = jQuery(this);
jQuery.removeData(this, 'mousePressed');
el.unbind('mousedown');
el.unbind('mouseup');
},
// our wrapper event is bound to "mousemove" and not "bind"
// change event type, so all attached drag handlers are fired
handler: function(event) {
event.type = 'drag';
jQuery.event.handle.apply(this, arguments);
}
};
Try something like this?
var isDown = false;
$(sel).mousedown(function() {
isDown = true;
});
$(sel).mouseup(function() {
isDown = false;
});
$(sel).mousemove(function() {
if (isDown) {
// Mouse is clicked and is moving.
}
});
If I'm reading your question correctly, you're asking about requiring the combination of multiple events to trigger a single function. It's possible to achieve this sort of thing, but I think it will depend greatly on the specific events and the logic or illogic of their combination. For example, the mousemove event:
...is triggered whenever the mouse
pointer moves, even for a pixel. This
means that hundreds of events can be
generated over a very small amount of
time.
Contrast that with the mousedown event, which is -- well, one per click. How to combine? The jQuery API goes on to state:
A common pattern is to bind the
mousemove handler from within a
mousedown hander [sic], and to unbind it
from a corresponding mouseup handler.
If implementing this sequence of
events, remember that the mouseup
event might be sent to a different
HTML element than the mousemove event
was. To account for this, the mouseup
handler should typically be bound to
an element high up in the DOM tree,
such as <body>.
Perhaps another approach would be to create a primitive finite state machine to ingest as inputs the various relevant events you have in mind, update its state accordingly, and then trigger a custom event when appropriate states are achieved. This all smells a little bit like reinventing the wheel, but maybe your requirement is very specific or unusual.
References: jQuery API: mousemove()
Alright, thanks for your idea Patrick. It reminded of a way I had done something like this in Java.
var m_down = false;
$(this).mousedown(function() {
m_down = true;
});
$(this).mouseup(function() {
m_down = false;
});
$(this).mousemove(function() {
// Code to occur here
});
Related
Is it ok to add a custom property to an existing DOM event? I would like to "mark" click events in a leaf element (say, a <span>) and catch the event (after bubbling) in an ancestor (say, <body>) and take a decision based on that mark.
Sure you can add new properties to an event.
If I understand you correctly you are looking for a way to uniquely identify a specific event so you can de-dupe or count unique events or whatever.
For instance if you have one event handler handling some events on different elements and you want to make sure you handle each event only once. If an event bubbles you might handle the same event on multiple elements. Some events might bubble, some might not (e.g. because another handler called Event.stopPropogation() lower in the dom tree. Maybe you don't have that much control over where you attach your event handlers and have to rely on bubbling in some cases.
Example...
<div id="parent">
<div id="child"></div>
</div>
var parentEl = document.querySelector('#parent'),
childEl = document.querySelector('#child');
child.addEventListener('click', function (e) {
var rand = ''+Math.floor(Math.random()*1000000);
console.log('child', rand);
e.custField = rand;
});
parent.addEventListener('click', function (e) {
console.log('parent', e);
});
child.click();
Downside of this is that you are polluting someone else's objects that might be handled by code that isn't your own. Not usually a great idea.
Alternatively
1) Use Event.timeStamp
Your event handler can keep a cache of Event.timeStamp and use that to de-dupe handled events.
var eventHandler = (function () {
var eventTimeStampCache = {};
return function (evt) {
if (evt.timeStamp in eventTimeStampCache) {
console.log('Ignore this event.', evt);
return;
}
eventTimeStampCache[evt.timeStamp] = true;
console.log('Handle this event.', evt);
};
})();
child.addEventListener('click', eventHandler);
parent.addEventListener('click', eventHandler);
child.click();
2) Use CustomEvent
If you are firing the events in the first place, use a CustomEvent that is all your own and feel free to make custom APIs and add event listeners that listen for your custom event.
I'd suggest firing a CustomEvent in response to the standard event except you have the same problem of knowing if this has been done already or not.
You can add a dataset item to the element called "data-xxx"
<span id="id" data-myvar="myvalue"></span>
And then later on
console.log( document.getElementbyID('id').dataset.myvar ) // myvalue
I use this D3 snippet to move SVG g elements to top of the rest element as SVG render order depends on the order of elements in DOM, and there is no z index:
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
I run it like:
d3.select(el).moveToFront()
My issue is that if I add a D3 event listener, like d3.select(el).on('mouseleave',function(){}), then move the element to front of DOM tree using the code above, all event listeners are lost in Internet Explorer 11, still working fine in other browsers.
How can I workaround it?
Single event listener on parent element, or higher DOM ancestor:
There is a relatively easy solution which I did not originally mention because I had assumed you had dismissed it as not feasible in your situation. That solution is that instead of multiple listeners each on a single child element, you have a single listener on an ancestor element which gets called for all events of a type on its children. It can be designed to quickly choose to further process based on the event.target, event.target.id, or, better, event.target.className (with a specific class of your creation assigned if the element is a valid target for the event handler). Depending on what your event handlers are doing and the percentage of elements under the ancestor on which you already are using listeners, a single event handler is arguably the better solution. Having a single listener potentially reduces the overhead of event handling. However, any actual performance difference depending on what you are doing in the event handlers and on what percentage of the ancestor's children on which you would have otherwise placed listeners.
Event listeners on elements actually interested in
Your question asks about listeners which your code has placed on the element being moved. Given that you do not appear concerned about listeners placed on the element by code which you do not control, then the brute-force method of working around this is for you to keep a list of listeners and the elements upon which you have placed them.
The best way to implement this brute-force workaround depends greatly on the way in which you place listeners on the elements, the variety that you use, etc. This is all information which is not available to us from the question. Without that information it is not possible to make a known-good choice of how to implement this.
Using only single listeners of each type/namespace all added through selection.on():
If you have a single listener of each type.namespace, and you have added them all through the d3.selection.on() method, and you are not using Capture type listeners, then it is actually relatively easy.
When using only a single listener of each type, the selection.on() method allows you to read the listener which is assigned to the element and type.
Thus, your moveToFront() method could become:
var isIE = /*#cc_on!#*/false || !!document.documentMode; // At least IE6
var typesOfListenersUsed = [ "click", "command", "mouseover", "mouseleave", ...];
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
var currentListeners={};
if(isIE) {
var element = this;
typesOfListenersUsed.forEach(function(value){
currentListeners[value] = element.selection.on(value);
});
}
this.parentNode.appendChild(this);
if(isIE) {
typesOfListenersUsed.forEach(function(value){
if(currentListeners[value]) {
element.selection.on(value, currentListeners[value]);
}
});
}
});
};
You do not necessarily need to check for IE, as it should not hurt to re-place the listeners in other browsers. However, it would cost time, and is better not to do it.
You should be able to use this even if you are using multiple listeners of the same type by just specifying a namespace in the list of listeners. For example:
var typesOfListenersUsed = [ "click", "click.foo", "click.bar"
, "command", "mouseover", "mouseleave", ...];
General, multiple listeners of same type:
If you are using listeners which you are adding not through d3, then you would need to implement a general method of recording the listeners added to an element.
How to record the function being added as a listener, you can just add a method to the prototype which records the event you are adding as a listener. For example:
d3.selection.prototype.recOn = function (type, func) {
recordEventListener(this, type, func);
d3.select(this).on(type,func);
};
Then use d3.select(el).recOn('mouseleave',function(){}) instead of d3.select(el).on('mouseleave',function(){}).
Given that you are using a general solution because you are adding some listeners not through d3, you will need to add functions to wrap the calls to however you are adding the listener (e.g. addEventListener()).
You would then need a function which you call after the appendChild in your moveToFront(). It could contain the if statement to only restore the listeners if the browser is IE11, or IE.
d3.selection.prototype.restoreRecordedListeners = function () {
if(isIE) {
...
}
};
You will need to chose how to store the recorded listener information. This depends greatly on how you have implemented other areas of your code of which we have no knowledge. Probably the easiest way to record which listeners are on an element is to create an index into the list of listeners which is then recorded as a class. If the number of actual different listener functions you use is small, this could be a statically defined list. If the number and variety is large, then it could be a dynamic list.
I can expand on this, but how robust to make it really depends on your code. It could be as simple as keeping tack of only 5-10 actually different functions which you use as listeners. It might need to be as robust as to be a complete general solution to record any possible number of listeners. That depends on information we do not know about your code.
My hope is that someone else will be able to provide you with a simple and easy fix for IE11 where you just set some property, or call some method to get IE to not drop the listeners. However, the brute-force method will solve the problem.
One solution could be to use event delegation. This fairly simple paradigm is commonplace in jQuery (which gave me the idea to try it here.)
By extending the d3.selection prototype with a delegated event listener we can listen for events on a parent element but only apply the handler if the event's target is also our desired target.
So instead of:
d3.select('#targetElement').on('mouseout',function(){})
You would use:
d3.select('#targetElementParent').delegate('mouseout','#targetElement',function(){})
Now it doesn't matter if the events are lost when you move elements or even if you add/edit/delete elements after creating the listeners.
Here's the demo. Tested on Chrome 37, IE 11 and Firefox 31. I welcome constructive feedback but please note that I am not at all familiar with d3.js so could easily have missed something fundamental ;)
//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetid, handler) {
return this.on(event, function() {
var eventTarget = d3.event.target.parentNode,
target = d3.select(targetid)[0][0];
if (eventTarget === target) {//only perform event handler if the eventTarget and intendedTarget match
handler.call(eventTarget, eventTarget.__data__);
}
});
};
//add event listeners insead of .on()
d3.select('#svg').delegate('mouseover','#g2',function(){
console.log('mouseover #g2');
}).delegate('mouseout','#g2',function(){
console.log('mouseout #g2');
})
//initial move to front to test that the event still works
d3.select('#g2').moveToFront();
http://jsfiddle.net/f8bfw4y8/
Updated and Improved...
Following Makyen's useful feedback I have made a few improvements to allow the delegated listener to be applied to ALL matched children. EG "listen for mouseover on each g within svg"
Here's the fiddle. Snippet below.
//prototype. move to front
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetselector, handler) {
var self = this;
return this.on(event, function() {
var eventTarget = d3.event.target,
target = self.selectAll(targetselector);
target.each(function(){
//only perform event handler if the eventTarget and intendedTarget match
if (eventTarget === this) {
handler.call(eventTarget, eventTarget.__data__);
} else if (eventTarget.parentNode === this) {
handler.call(eventTarget.parentNode, eventTarget.parentNode.__data__);
}
});
});
};
var testmessage = document.getElementById("testmessage");
//add event listeners insead of .on()
//EG: onmouseover/out of ANY <g> within #svg:
d3.select('#svg').delegate('mouseover','g',function(){
console.log('mouseover',this);
testmessage.innerHTML = "mouseover #"+this.id;
}).delegate('mouseout','g',function(){
console.log('mouseout',this);
testmessage.innerHTML = "mouseout #"+this.id;
});
/* Note: Adding another .delegate listener REPLACES any existing listeners of this event on this node. Uncomment this to see.
//EG2 onmouseover of just the #g3
d3.select('#svg').delegate('mouseover','#g3',function(){
console.log('mouseover of just #g3',this);
testmessage.innerHTML = "mouseover #"+this.id;
});
//to resolve this just delegate the listening to another parent node eg:
//d3.select('body').delegate('mouseover','#g3',function(){...
*/
//initial move to front for testing. OP states that the listener is lost after the element is moved in the DOM.
d3.select('#g2').moveToFront();
svg {height:300px; width:300px;}
rect {fill: pink;}
#g2 rect {fill: green;}
#testmessage {position:absolute; top:50px; right:50px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="svg">
<g id="g1"><rect x="0px" y="0px" width="100px" height="100px" /></g>
<g id="g2"><rect x="50px" y="50px" width="100px" height="100px" /></g>
<g id="g3"><rect x="100px" y="100px" width="100px" height="100px" /></g>
</svg>
<div id="testmessage"></div>
As with all delegated listeners if you move the target element outside of the parent you have delegated the listening to then naturally the events for that child are lost. However, there is nothing to stop you delegating the event listening to the body tag as you'll never move a child outside of that. EG:
d3.select('body').delegate('mouseover','g',function(){...
This also happens in IE prior to 11. My mental model for why this error occurs is that if you're hovering over an element and then move it to the front by detaching and re-attaching it, mouseout events won't fire because IE loses the state that a mouseover occured in the past and thus doesn't fire a mouseout event.
This seems to be why it works fine if you move all other elements but the one you're hovering over. And this is what you can easily achieve by using selection.sort(comparatorFunction). See the d3 documentation on sort and the selection.sort and selection.order source code for further details.
Here's a simple example:
// myElements is a d3 selection of, for example, circles that overlap each other
myElements.on('mouseover', function(hoveredDatum) {
// On mouseover, the currently hovered element is sorted to the front by creating
// a custom comparator function that returns “1” for the hovered element and “0”
// for all other elements to not affect their sort order.
myElements.sort(function(datumA, datumB) {
return (datumA === hoveredDatum) ? 1 : 0;
});
});
In jQuery, you can do the following:
$('#j_unoffered').on('click', '.icon_del', function () {...
This puts one handler on the element j_unoffered that fires if any descendant element with class icon_del is clicked. It applies, furthermore, to any subsequently created icon_del element.
I can get this working fine in Closure where the click is on the element itself.
goog.events.listen(
goog.dom.getElement('j_unoffered'),
goog.events.EventType.CLICK,
function(e) {...
How can I specify a parent event target in Closure that works for its children/descendants in the same way as the jQuery example?
I'm assuming I need to use setParentEventTarget somehow, but I'm not sure how to implement it for DOM events. Most of the documentation I've found pertains to custom dispatch events.
-- UPDATE --
I'm wondering if there is anything wrong with this rather simple solution:
goog.events.listen(
goog.dom.getElement('j_unoffered'),
goog.events.EventType.CLICK,
function(e) {
if (e.target.className.indexOf('icon_del') !== -1) {...
It still leaves this bound to the parent, but e.target allows a work-around. The fifth argument in listen (opt_handler) allows you to bind this to something else, so I guess that's an avenue, too.
I don't know about such possibility too, so I suggest other piece of code:
var addHandler = function(containerSelector, eventType, nestSelector, handler) {
var parent = goog.isString(containerSelector) ?
document.querySelector(containerSelector) :
containerSelector;
return goog.events.listen(
parent,
eventType,
function(e) {
var children = parent.querySelectorAll(nestSelector);
var needChild = goog.array.find(children, function(child) {
return goog.dom.contains(child, e.target);
});
if (needChild)
handler.call(needChild, e);
});
});
Usage:
addHandler('#elem', goog.events.EventType.CLICK, '.sub-class', function(e) {
console.log(e.target);
});
Update:
If you will use this e.target.className.indexOf('icon_del') there will be possibility to miss the right events. Consider a container div with id = container, it has couple of divs with class innerContainer, and each of them contains couple of divs with class = finalDiv. And consider you will add event handler with your code above, which will check e.target for innerContainer class. The problem is when user will click on finalDiv your handler will be called, but the event target will be finalDiv, which is not innerContainer, but contained by it. Your code will miss it, but it shouldn't. My code checks if e.target has nested class or contained by it, so you will not miss such events.
opt_handler can't really help you either, because there might be many nested elements you want to hanlde (which of them will you pass here? maybe all, but not that helpful, you can get them in event handler whenever you want), moreover they can be added dynamically after, so when you add handler you could not know about them.
In conclusion, I think doing such a job in an event handler is justified and most efficient.
What you are referring to is called event delegation
It seems that this is not possible (out of the box) with Google Closure Library; So my recommandation is to use jQuery or another similar event handling library that offers this functionality. If this is not possible or if you wanna do it by hand here's one possible approach (NOTE: this is not for production use)
var delegateClick = function(containerId, childrenClass, handler){
goog.events.listen(goog.dom.getElement(containerId), goog.events.EventType.CLICK, function(event){
var target = event.target;
//console.log(event);
while(target){
if ( target.className && target.className.split(" ").indexOf(childrenClass)!== -1) {
break;
}
target = target.parentNode;
}
if(target){
//handle event if still have target
handler.call(target, event);
}
});
}
//then use it, try this here: http://closure-library.googlecode.com/git/closure/goog/demos/index.html
//..select the nav context
delegateClick( 'demo-list' ,'goog-tree-icon', function(event){console.log(event);})
Here's a more in depth analysis of event delegation
Again, you should use a proven library for this, here are some options: jQuery, Zepto, Bean
I have an introduction on my page which shall disappear when a key is pressed or a certain elements is clicked on. I'll use the same function for both events but as the page is heavily modified the event shall fire only once regardless which way it was triggered.
My function:
function start() {
$('.intro').remove();
$('.something-else').show();
}
How I bind the events:
$('body').keypress(start);
$('.intro').click(start);
If the events were the same I could say
$('body, .intro').one('click', start);
If both events were to happen to the same element I could say:
$('.intro').one('click keypress', start);
How to combine both things: having different elements and different events and the function must only be called once?
The simplest solution would be to store whether your function has been called or not.
var started = false;
function start() {
if(started) return;
$('.intro').remove();
$('.something-else').show();
started = true;
}
Then your function can only be called once, every other call will be ignored.
I don't think this solution is a clean as an explicit approach, but is done using a combination of .one() and .trigger(). Because you call $(".intro").remove() on one of the bound items the event handler is implicitly unbound. Sample Fiddle
Excluding the html seen in fiddle, this is the code.
// Your event handler
function start() {
$("#intro").remove();
$("#main").show();
console.log("start handler called");
}
// Handle the keypress event
$("body").one( "keypress", start );
// Allow user to click through the intro, but trigger the keypress handler on the body which is a one-time event handler)
$("#intro").one( "click", function () { $("body").trigger( "keypress" ); });
Use two lines of code and be done ;)
$('body').one('keypress', start);
$('.intro').one('click', start);
Edit:
You should manually unbind the other callback, lest it runs later, unexpectedly.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
jQuery - How can I bind all events on a DOM element?
Imagine, if we want to make some element completely initeractable.
We could, of course bind a prevent default for a click event as follows:
$('form *').bind('click', function(event) {
event.preventDefault();
});
But that's just one event, and there are many more, like hover, focus, selectstart, and many more.
We could specify them all in one line like 'click focus hover dblclick blur selectstart' but that doesn't make much sense and is not easy to maintain.
So, is it possible to bind an event listener without discriminating for the type of the event? Maybe some native JavaScript listeners allow it?
No such possibility because not all elements support same events and not all events behave in the same way. You always have to explicitly provide a list of events whether defined statically or dynamically by a script that spits out event names.
Even though I linked to a script that creates an array of event names, these are made on one element only. You should of course be generating this with a more complex and slower script that enumerates over all elements in question and adds missing events. Using Javascript objects as associative array for faster searching whether a particular event has been added or not.
A better suggestion
What you're trying to do is likely a highly over-engineered solution. When I'm creating a demo clickable interface that should disable some elements (be it links, buttons or anything else) I rather do it by defining a CSS class that disables an element in question and have a simple script that does disabling afterwards.
You could leverage this even further by also providing which events you'd like to disable on particular element (with default being a click event).
<!-- no events; use defaults -->
No-follow link
<button class="disable">Nothing happens</button>
<!-- provide events -->
No-follow link
<form class="disable" data-events="submit">...</form>
Script
$(function() {
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(ctx.data("events") || "click", disable);
});
});
Using smart defaults
Upper example defines one single event default. click event. This is fine and works in majority of cases, but not in all. form elements for instance would always have to define submit event that should be disabled. So. Smart defaults then. We should also consider the fact that list events that need supression is usually short. And if we cover majority of cases using defaults we only have a small overhead on those elements that actually do deviate from defaults.
$(function() {
// click is still default event
// this object defines per element events that aren't just click
var extraDefaults = {
form: "submit"
};
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(
// use inline-defined events
ctx.data("events") ||
// use extra defaults if present
extraDefaults[this.tagName.toLower()] ||
// just use default click event
"click",
disable);
});
});
You can bind most jQuery events like this :
$("#elementID").on(Object.keys(jQuery.event.fixHooks).join(" "), function(e) {
e.preventDefault();
});
This will preventDefault on the following events :
click dblclick mousedown mouseup mousemove mouseover mouseout
mouseenter mouseleave keydown keypress keyup contextmenu
FIDDLE
Well after considering all the options, it still does not look convenient for all this event hustling. As it also has to bind the handlers for each event individually the script will hit the performance as well.
I am going to stick with a much simpler solution, just putting a div with transparent bg on top to cover our element.
$('form').css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url(1px_transparent.png);"></div>'));
Which is going to automatically fill the whole area of the element, alternatively, we can use a half-transparent picture so it will be also understood by a user that this is locked element, and would not cause confusion.
And to unlock we simply remove the .mask div from our element.
EDIT
New Fiddle: http://jsfiddle.net/YAdXk/8/
Actually we can disable tabbing by setting tabindex attribute to -1
.find('input,textarea,select').attr('tabindex','-1');
The updated fiddle prevents from tabbing as well.
EDIT2
OR, we can extend jQuery to use our custom lock() and unlock() functions on any element.
See the last fiddle: http://jsfiddle.net/YAdXk/13/
(function($) {
$.fn.lock= function() {
return this.each(function() {
$(this).css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url('+transparent_picture+');"></div>')).find('input,textarea,select').attr('tabindex','-1');
});
};
$.fn.unlock= function() {
return this.each(function() {
$(this).find('*').removeAttr('tabindex').filter('.mask').remove();
});
};
})( jQuery )
var all_events = "click blur focus mouse"; //etc...
$('form *').bind(all_events, function(event) {
event.preventDefault();
});
Now is easier to maintain ;)
jQuery defines all shortcut event types here, so you can use that string to store all events for re-use:
var events = "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu";
$('button').bind(events, function() {
// hey
});
Yes, it is possible, to catch all events of one type at once! But you'll need to specify all the event types explicitly.
Your code example of "form *" is inefficient, and would not catch events on elements that are added after your code executes.
Because of the bubbling effect of javascript events, you can assign a catch all event handler on the most parent element, eigther $("form") or $("body"), and add preventDefault() to that.
Example code:
$("a").on("click", function() {
$("body").append("<p>Clicked...</p>");
});
$("body").on("click", function(e) {
e.preventDefault();
});
with:
<div>
<p>Click on me</p>
</div>
On JSfiddle: http://jsfiddle.net/erlang/EHeBK/
The concept of catching all events on a parent element, is often referred to as event delegation.