PDA

View Full Version : Calling function on click except when click falls on element with default action


gsnedders
05-29-2009, 12:17 AM
Um, well, the title really says it all. But to those who don't quite comprehend what I mean:

I want to call a function on an element when a click event is fired, except when that click falls on an element with what the spec calls a "default action" (e.g., clicking on an a element submits the link).

The only way I can think of doing this is to add an event listener to the element on both capture and bubbling phases, on the capture phase setting some variable like doCall = true, then seeing on the bubble phase if it is equal to true, having added event listeners on all elements which HTML 5 puts in the "Interactive content" category, like a and input which sets doCall = false.

This seems needlessly complex, so if anyone can come up with a nicer solution I'd be grateful.

itsallkizza
05-29-2009, 12:38 AM
I don't think it'd be too complex to simply assign every element matching certain criteria (ie not having your "default action") an onclick handler on page load. Or assign every element an onclick which in turn checks to see if it is of this "default action" class.

Here's an example in jQuery:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Test</title>
<style type="text/css">
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
// <![CDATA[

$(document).ready(function(){
$("body *").click(function(){
var classes = this.className.split(" ");
var is_default_action = false;
for (var i=0;i<classes.length;i++)
{
if (classes[i] == "default_action") is_default_action = true;
}
if (!is_default_action)
{
//do something
}
});
});

// ]]>
</script>
</head>
<body>

<h1>my webpage</h2>
<h2>your source for all kinds of cool stuff</h2>
<br />
<h4>Here's Some Cool Stuff Now</h4>
<p>try this site out, it's pretty sweet: <a href="http://www.google.com" class="default_action">google.com</a></p>

</body>
</html>

gsnedders
05-29-2009, 12:44 AM
"Default action" is not a class though, it's a property which exists within the browser. I don't think there's any way to tell if an element has a default action.

itsallkizza
05-29-2009, 04:17 PM
If there's no way to tell natively whether or not an element falls under this "has default action" category, then it's a human description. So you, as the developer, would then need to define this category in your script. I can't expect the browser to know, for example, what all the web 2.0 elements are on a page. If I wanted to do something with say, all the bubbly buttons but not the 2D buttons, I would need to define the bubbly ones myself. Assigning a classname is an excellent method of doing this because it allows easy manipulation of these elements in JS.

gsnedders
05-29-2009, 07:39 PM
If there's no way to tell natively whether or not an element falls under this "has default action" category, then it's a human description. So you, as the developer, would then need to define this category in your script. I can't expect the browser to know, for example, what all the web 2.0 elements are on a page. If I wanted to do something with say, all the bubbly buttons but not the 2D buttons, I would need to define the bubbly ones myself. Assigning a classname is an excellent method of doing this because it allows easy manipulation of these elements in JS.

I don't believe there to be a way to tell if an element has a default action, but I could be wrong; I can at least just match all the interactive elements and work from that (but that isn't entirely fun matching them all). The browser itself does know if an element has a default action (this is what "return false" in a click event stops from happening), it's just I don't know of any way to find out through ECMAScript whether an element has a default action. Class names only work when you are dealing with content you create yourself, and isn't syndicated from elsewhere, which is why I'd need to match each and every interactive element. Oh, actually, I think DOMActivate does what I want, looking at the spec again.

itsallkizza
05-29-2009, 08:30 PM
You could make a list of elements you know have this default action (anchors, forms, anything else?) and then add the onclick to all other elements. That'd be pretty straightforward.

EDIT: Or you could test for arguments[0] on the onclick. If it exists, don't run the function.

gsnedders
05-29-2009, 08:34 PM
OK, so actually playing around with implementation of this, DOMActivate doesn't help (as it is fired after click), so the best I could do was what I suggested in the first post, i.e.,

<!doctype html>
<title>Test</title>
<style>
div {
border: 1px solid red;
padding: 100px;
}
p {
border: 1px solid blue;
padding: 10px;
}
</style>
<script>
window.onload = function() {
var doEvent = false;
var divCapture = function(e) {
doEvent = true;
}
var divBubble = function(e) {
if (doEvent) {
alert("foo");
}
}
var activation = function(e) {
doEvent = false;
}
document.getElementsByTagName("div")[0].addEventListener("click", divCapture, true);
document.getElementsByTagName("div")[0].addEventListener("click", divBubble, false);
var children = document.getElementsByTagName("div")[0].querySelectorAll("a, img[usemap], video[controls], audio[controls], label, input:not([type=hidden]), button, select, textarea, keygen, details, datagrid, bb, menu[type=toolbar]");
for (i = 0; i < children.length; i++) {
children[i].addEventListener("click", activation, false);
}
}
</script>
<div>
<p>Lipsum <a href="http://example.com">Foo</a>bar</p>
</div>

adios
06-01-2009, 08:04 PM
Just a random thought here, but, how about setting a timer on the onclick execution? If there's no default action, the timer expires and your handler runs; if there is, the page is dumped and the timer along with it. I've used this "reversal of execution" on a lot of menu scripts to deal with mouseouts, e.g., and can recommend it as a hack.

<a href="blah.html" onclick="setTimeout(doSomething,50);return true;">

btw, kudos for saying 'when a click event is fired' ... nice to see someone using the appropriate terminology.

itsallkizza
06-02-2009, 05:02 PM
I have a strict no setTimeout policy unless used for animation ;)

adios. good thinking, but your method would fail if
a) the element had a default action, but in a particular case the developer wanted to return false to prevent it from firing, or
b) the default action was a link out (anchor or form) that had "_blank" as it's target - the code would still run on the parent page.

again, why not

you could test for arguments[0] on the onclick. If it exists, don't run the function.