Overwriting tap event with preventDefault - javascript

I had a lot of:
$('#element').on('tap', function(){
// some code ..
})
I searched many questions about the tap event problem firing twice, and I solved my problem using e.preventDefault(), now I have a lot of:
$('#element').on('tap', function(e){
e.preventDefault();
// some code ..
})
Ok, but as I said, I have many of these calls and I don't like much to write every time e.preventDefault(), then I typed $.fn.tap on chrome's console and it showed me:
function (a){return a?this.bind(c,a):this.trigger(c)}
I tried to overwrite it this way:
$.fn.tap = function (a) {
a.preventDefault();
return a?this.bind(c,a):this.trigger(c)
}
But it didn't worked as it did in the previous e.preventDefault().
I'm not seeing anything obvious and I'm out of ideas for this.
Any help or idea is appreciated. Thanks in advance.

This is how you can create your $.fn.tap:-
$.fn.tap = function(f) {
$(this).on('tap', function(e) {
e.preventDefault();
f();
});
return this;
};
//usage
$('.selector').tap(function() {
alert('foo bar')
})
#Washington Guedes - overwrite the default tap-event to always use e.preventDefault()
rather than changing from $(element).on('tap', function(){}) to
$(element).tap(function(){})
You could add a delegate event to body for tap, without specifying a target. This will then fire for all tap events on the body, which you can then check if the target has its own tap event, so you can then e.preventDefault();.
NOTE: This will not work for delegated tap events as shown.
// global tap handler
$('body').on('tap', function(e) {
if ($._data(e.target, "events").tap)
e.preventDefault();
});
// tap event
$('a.tap').on('tap', function(e) {
$(this).css('color', 'red');
});
// delegated tap event
$('body').on('tap', 'a.delegate', function(e) {
$(this).css('color', 'green');
});
a {
display: block;
margin: 20px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.js"></script>
<a class="tap" href="www.google.co.uk">tap event, prevented.</a>
<a class="delegate" href="www.google.co.uk">delegate tap event, not prevented.</a>
no tap event, not prevented

One of the cool features of jQuery (I usually don't use 'jQuery' and 'cool' in a single sentence) is that it lets you specify custom behaviour for events, using the $.event.special object.
There is very little documentation on the subject, so a little example would be in order.
A working fiddle using the click event (as this was more convenient for me to write on my laptop) can be found here
Translated to your request to have all tap events have e.preventDefault() invoked before the actual handler, would look like:
$.event.special.tap.add = function(options) {
var handler = options.handler;
options.handler = function(e) {
e.preventDefault();
return handler.apply(this, arguments);
}
}
What this code does (should do, as I haven't actually tested the tap version above) is telling jQuery that you want a special treatment for the tab event, more particularly you want to provide a 'wrapping handler' which does nothing more than call e.preventDefault() before calling the provided event handler.
UPDATE: prevented the default tap-settings from being overwritten, for future visitors
NOTE: Before you make any attempt on changing the default behaviour of things, you should ask yourself why the defaults don't work for you. Mostly because changing default (=expected) behaviour will upset your future self (or worse, another person) while maintaining your code and adding features.
In order to create a maintainable (and predictable) flow in your code the suggested solution to create a special case function ($.fn.tap) is in fact a very viable solution, as it does not interfere with the default (=expected) behaviour of things.
From the links I provided you should also be able to create your own event type (e.g. tapOnly) and make it more obvious there is some custom work involved. Then again, both of these solutions will require you to change your event bindings, which is what you are trying to prevent.

I knew can be a bad idea but I've just tested this in Chrome
$('*').on('tap',function(e){
e.preventDefault();
});
$('#element').on('tap', function(){
// some code ..
});
and if you don't need this for all elements:
$('*').not('#aCertainElement').on('tap',function(e){
e.preventDefault();
});

I had a similar problem, in which e.preventDefault() would work on some cases, but not on others. It showed no errors, and using try-catch was not displaying the catch alert. Adding e.stopImmediatePropagation() did the trick, in case it helps anyone

Related

Extend jQuery's .on() method with custom events

There are numerous questions on SO about custom jQuery events. Almost all of them only have answers suggesting the use of $.fn.extend().
Here is my script using $.fn.extend() to detect when an element has stopped scrolling:
$.fn.extend({
scrollStopped: function (callback) {
var $this = $(this)
$this.scroll(function () {
clearTimeout($this.data('scrollTimeout'))
$this.data('scrollTimeout', setTimeout(callback.bind(this), 150))
}.bind(this))
return this
}
})
$('element').scrollStopped(function () {
alert('Scrolling has stopped')
})
Like the title of this question suggests, what I want, however, is not an event that has to be added with .scrollStopped(), but one that can be added with the .on() method, like so:
$('window').on('scrollStopped', function () {
alert('Window has stopped scrolling')
})
$('div').on('scrollStopped', function () {
alert('Div has stopped scrolling')
})
I've been scanning the jQuery source trying to find a way to do this. After copy-pasting a bunch of snippets for an hour or two, I decided to look for an answer on SO, which was surprisingly hard to find. This answer helped me a bit, but (like I said in the comments of that answer, one hour ago) the events that trigger the custom event there are hardcoded to the body and then sent to the elements to which the .on() method is bound.
There are two problems with the approach:
The event logic still isn't part of jQuery's internal structure. It's simply part of an event triggered by the body (not a deal-breaker per say)
Scroll events (like the one I'm trying to use) don't bubble, meaning that converting the events from keydown to scroll won't work
I've simplified the Fiddle from the answer here.
I know I could use a native event and set useCapture to true to capture all scroll events on the page, but seeing there are no answers anywhere on SO that explain how to do what I want, I think it's worthwhile to take a look at this.
The only reason I want to do this, is because I want to be able to write things like:
$(window).on('beforeunload scrollStopped', function () { savePageScroll() })
Instead of:
$(window)
.on('beforeunload', function () { savePageScroll() })
.scrollStopped( function () { savePageScroll() })`

Method for triggering certain click event using .trigger() but suppressing all others

My situation is that I am trying to trigger a single event using the jQuery .trigger() method. However the element I am triggering has multiple click event listeners.
Actually finding what these listeners are and what they trigger from the source code is probably not viable as its included in the sites main JS file and its all minified and pretty much unreadable.
At the moment I know that the element when clicked performs some kind of ajax call and loads more data into the DOM of the page (which is what i want to trigger), however it also displays an overlay (which is what I want to suppress temporarily).
As its just an overlay there are workaround I can make; using a display:none on it straight after click etc. However it would be much more elegant if i could somehow suppress all click events on this element except the desired event.
Any ideas if this is actually possible? And if so how I would go about it?
You need to register your own event at the top of the event chain. And cancel the event chain in your event. Here is a solution with writing a custom jquery extention.
$.fn.bindFirst = function (which, handler) {
var $elm = $(this);
$elm.unbind(which, handler);
$elm.bind(which, handler);
var events = $._data($elm[0]).events;
var registered = events[which];
registered.unshift(registered.pop());
events[which] = registered;
}
$("#elm").bindFirst("click", function(e) {
// edit: seems like preventing event does not work
// But your event triggers first anyway.
e.preventDefault();
e.stopPropagation();
});
Reference:
https://gist.github.com/infostreams/6540654
EDIT:
https://jsfiddle.net/8nb9obc0/2/
I made a jsFiddle and it seems like event preventing does not work in this example. There might be another solution.

Is there a standard for the event order of click and change on a checkbox?

I've noticed that the order of 'click' and 'change' events are different in Chrome and Firefox.
See this JSFiddle for example: http://jsfiddle.net/5jz2g/3/
JavaScript:
var el = $('foo');
var fn = function(e) {
console.log(e.type);
}
el.addEvent('change', fn);
el.addEvent('click', fn);
In Chrome this logs:
change
click
And in Firefox this logs:
click
change
Is there a standard for the order of events? Which should fire first? The MDN doesn't seem to mention this and I couldn't find a thing about this in the W3C documents.
DOM3 Events document has a recommendation about events order. According to it, in case of checkbox, the correct order will be click then change and not vice versa, because change event obviously is a part of default actions for checkbox, see Example 5 and fiddle, which works as expected in FF but not in Chrome. That's logical, anyway. But! Let's read default actions section carefully:
Default actions should be performed after the event dispatch has been completed, but in exceptional cases may also be performed immediately before the event is dispatched.
Did you see that? W3C uses RFC's words SHOULD and MAY in one sentence! Nothing to be done, they're cautious guys. IMO, that's why we have what we have :)
No, but if you want to make it consistent, you can fire one event off the other rather than having them fire separately since for a check box and change amount to the same event...or perhaps you could use an interval timer to to delay the response on one of the events.
try this
$(function(){
var $select = $('foo');
var store = function(e) {
console.log(e.type);
};
$select.addEvent('change', store).addEvent('click', store);
});
http://jsfiddle.net/donddoug/Du7z5/
Couldn't find a way to sync events cross browser...
the following does work in the same order in both FF and Chrome;
JavaScript Code (use mouseup / mousedown to catch the "click"):
var el = $('foo');
var fn = function(e) {
console.log(e.type);
}
el.addEvent('change', fn);
el.addEvent('mouseup', fn);
http://jsfiddle.net/5jz2g/17/

Prevent click jump on jQuery one() function

I'm using the one() function in jQuery to prevent multiple clicks. However, when they click the element a second time, it does the annoying click jump and sends you back to the top of the page.
Is there a way to prevent this from happening? Unbinding the click and re-binding it when the function is done has the same result (and I'm assuming that one() just unbinds the event anyways).
A quick example of this happening: http://jsfiddle.net/iwasrobbed/FtbZa/
I'm not sure if this is better or not, but you could bind a simple function that maintains the return false.
jQuery provides a shortcut for this with .bind('click',false).
$('.someLink').one('click', function() {
$(this).bind('click',false);
return false;
});
or if you have several of these links, a very efficient alternative would be to use the delegate()[docs] method to bind a handler to a common ancestor that takes care of the return false; for you.
Here I just used the body, but you could use a nearer ancestor.
$('.someLink').one('click', function() {
});
$('body').delegate('.someLink','click',function(){return false;});
Try changing the href so the '#' isn't being used: http://jsfiddle.net/FtbZa/1/
$('.someLink').one('click', function() {
alert('test');
return false;
}).attr('href', 'javascript:void(0);');
You could use the standard .click() function and a little logic:
1. $('.someLink').click(function(event) {
2. event.preventDefault();
3. if (!$(this).hasClass("clicked"))
4. alert('This will be displayed only once.');
5. $(this).addClass("clicked");
});
Listen to anything with the class someLink for a .click()
Stop the browser doing what it would normally do.
Check if the object has the class clicked (Note this could be any name you wanted)
It hasn't so do something.
Add the class clicked so next time its clicked, it will ignore your code.
See the demo here
The problem is that after the listener has been unbound there is nothing stopping the browser from honoring the link (Which it is treating as an anchor tag) and trying to go to it. (Which in this case will simply lead to the top of the page.

Properly bind Javascript events

I am looking for the most proper and efficient way to bind Javascript events; particularly the onload event (I would like the event to occur after both the page AND all elements such as images are loaded). I know there are simple ways to do this in jQuery but I would like the more efficient raw Javascript method.
There are two different ways to do it. Only one will work; which one depends on the browser. Here's a utility method that uses both:
function bindEvent(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
element.attachEvent('on'+type, handler);
}
}
In your case:
bindEvent(window, 'load', function() {
// all elements such as images are loaded here
});
I know you did only ask about how to bind events. But Ooooo boy the fun doesn't end there. There's a lot more to getting this right cross-browser than just the initial binding.
#d.'s answer will suffice just fine for the specific case of the load event of window you're looking for. But it may give novice readers of your code a false sense of "getting it right". Just because you bound the event doesn't mean you took care to normalize it. You may be better of just fixing window.onload:
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
// now do your thing here
}
}(window.onload))
But for the general case of event binding #d.'s answer is so far from satisfying as to be frustrating. About the only thing it does right is use feature-detection as opposed to browser detection.
Not to go on too much of a rant here but JavaScript event binding is probably the #1 reason to go with a JavaScript library. I don't care which one it is, other people have fixed this problem over and over and over again. Here're the issues in a home-brewed implementation once inside your handler function:
How do I get a hold of the event object itself?
How do I prevent the default action of the event (eg clicking on a link but not navigating)
Why does this point to the window object all the time?
(Re mouse events) What are the x/y coords of the event?
Which mouse button triggered the event?
Was it a Ctrl-click or just a regular click?
What element was the event actually triggered on?
What element is the event going to? (ie relatedTarget, say for blur)
How do I cancel the bubbling of the event up through its parent DOM?
(Re event bubbling) what element is actually receiving the event now? (ie currentTarget)
Why can't I get the freaking char code from this keydown event?
Why is my page leaking memory when I add all these event handlers?? Argh!
Why can't I Unbind this anonymous function I bound earlier?
And the list goes on...
The only good reason to roll your own in these days is for learning. And that's fine. Still, read PPK's Intro to browser events. Look at the jQuery source. Look at the Prototype source. You won't regret it.
Something like that
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
$(".loading-overlay .spinner").fadeOut(300),
$(".loading-overlay").fadeOut(300);
$("body").css({
overflow: "auto",
height: "auto",
position: "relative"
})
}
}(window.onload));
window.onload = function() {
// ...
};

Categories