jQuery click event triggered multiple times off one click - javascript

I am having trouble with multiple clicks being registered in jQuery when only one element has been clicked. I have read some other threads on Stack Overflow to try and work it out but I reckon it is the code I have written. The HTML code is not valid, but that is caused by some HTML 5 and the use of YouTube embed code. Nothing that affects the click.
The jQuery, triggered on document.ready
function setupHorzNav(portalWidth) {
$('.next, .prev').each(function() {
$(this).click(function(e) {
var target = $(this).attr('href');
initiateScroll(target);
console.log("click!");
e.stopPropagation();
e.preventDefault();
return false;
});
});
function initiateScroll(target) {
var position = $(target).offset();
$('html, body').animate({
scrollLeft: position.left
}, 500);
}
}
Example HTML
<nav class="prev-next">
Prev
Next
</nav>
In Firefox one click can log a "Click!" 16 times! Chrome only sees one, but both browsers have shown problems with the above code.
Have I written the code wrongly or is there a bug?
-- Some extra info ------------------------------------------
setupHorzNav is called by another function in my code. I have tested this and have confirmed it is only called once on initial load.
if ( portalWidth >= 1248 ) {
wrapperWidth = newWidth * 4;
setupHorzNav(newWidth);
}
else
{
wrapperWidth = '100%';
}
There are mutiple instances of nav 'prev-next'. All target different anchors. All are within the same html page.
<nav class="prev-next">
Prev
</nav>

Try unbinding the click event like this
$(this).unbind('click').click(function (e) {
});

You don't need .each() for binding event handlers. Try this instead:
$('.next, .prev').click(function(e){
var target = $(this).attr('href');
initiateScroll(target);
console.log("click!");
e.stopPropagation();
e.preventDefault();
return false;
});
EDIT:
I think it is the way you are attaching the event handler from within the setupHorzNav function that is causing it. Change it to attach it only once from say, $(document).ready() or something.
I have managed to get the situation of multiple event handlers by attaching the event handlers from a function that gets called from event handler. The effect is that the number of click event handlers keeps increasing geometrically with each click.
This is the code: (and the jsfiddle demo)
function setupNav() {
$('.next, .prev').each(function () {
$(this).click(function (e) {
setupNav();
var target = $(this).attr('href');
console.log("click!");
e.stopPropagation();
e.preventDefault();
return false;
});
});
}
setupNav();
See how calling the setupNav() function from the click event handler adds multiple eventhandlers (and the click log message) on successive clicks

Since it is not clear from your question whether you are calling the binding function multiple times, a quick and dirty fix would be:
$('.next, .prev').unbind('click').click(function() {
...
});
What you are doing here is unbinding any previously bound event handlers for click and binding afresh.

Are there no other click bindings elsewhere?
Are you loading the page with ajax?
You could also try this:
$('.next, .prev').click(function (e) {
var target = $(this).attr('href');
initiateScroll(target);
console.log("click!");
e.stopPropagation();
e.preventDefault();
return false;
});

Related

jQuery code does not work

I tried to implement this javascript code taken from the other thread here:
css 'pointer-events' property alternative for IE, but I cannot make it work. When I click the target iframe element, the desired "click-through" effect did not come to reality. I tried it in jsfiddle.com too but it doesn't work either... I must have missed something important. Can someone show me how to do this code correctly in jsfiddle? Thank You very much.
$(document).ready(function() {
$(document).on('mousedown', '#iframe_1', function (e) {
$(this).hide();
var BottomElement = document.elementFromPoint(e.clientX, e.clientY);
$(this).show();
$(BottomElement).mousedown(); //Manually fire the event for desired underlying element
return false;
});
});
The iframe won't even hide... https://jsfiddle.net/c4vttL6t/
The event is not fired because you have to register the event on the content of the iframe, not in the iframe itself. You can achieve this with something like this:
var iframeBody = $('body', $('#iframe-1')[0].contentWindow.document);
$(iframeBody).on('mousedown', function(event) {
$('#iframe-1').hide();
return false;
});
But beware, if you are loading content from other site, this could not work.
Code taken from here
Update: Maybe this code could help you, taking into consideration that the iframe are one on top of the other:
// iframe-1 mousedown handler
var iframeBody1 = $('body', $('#iframe-1')[0].contentWindow.document);
$(iframeBody1).on('mousedown', function(e) {
//$('#iframe-1').hide();
// alert mousedown 1
alert('mousedown 1');
return false;
});
// iframe-2 mousedown handler
var iframeBody2 = $('body', $('#iframe-2')[0].contentWindow.document);
// search and trigger mousedown handler on X element
var iframeDocument1 = $("#iframe-1")[0].contentWindow.document;
// iframe-2 mousedown handler
$(iframeBody2).on('mousedown', function(e) {
//$('#iframe-2').hide();
// calculate position relative to element if necessarily
$(iframeDocument1.elementFromPoint(e.pageX, e.pageY)).mousedown();
// Alert mousedown 2
alert('mousedown 2');
// as you are using iframe I think hide and show is not necessary
// because you can trigger mousedown handler on a particular element
//$('#iframe-2').show();
return false;
});

jQuery mouse event blocks default propagation

I have an issue with a full-body-overlay effect when clicking on any link.
The wanted effect is:
when any link of the page is "pushed" (mousedown or touchstart), an image overlay appears on the top of the whole body (hiding the complete content of the page)
when the link is (or should be) "released" (mouseup or touchend), the overlay should disappear and the original clicked link target should happen (the "href" attribute must be followed for instance).
The first part is OK: I added an event handler listening the "mousedown touchstart" events on every links. The second part is more complicated as the overlay does not trigger the "release" action of the link (the overlay is over the content with the link). So I attached a "mousedown touchend" event handler on the whole document, which works, but the default behavior of the clicked link (go to its "href" attribute for instance) is never fired :(
Here is my JS:
$(document).ready(function(){
// the full-page overlay selector
var $overlay = $('.body-overlay');
// a flag to keep a trace of clicked element
var overlay_triggered = false;
// the "mousedown" event handler
function show_overlay(evt)
{
var $target = $(evt.target);
if (overlay_triggered === false) {
$overlay.show();
overlay_triggered = $target;
$(document).on('mouseup touchend', hide_overlay);
}
return true;
}
// the "mouseup" event handler
function hide_overlay(evt)
{
if (overlay_triggered !== false) {
$(document).off('mouseup touchend', hide_overlay);
$overlay.hide();
// !!! - here I try to trigger a classic click but it never works
$(overlay_triggered).click();
overlay_triggered = false;
}
return true;
}
// attachment of the mousedown handler on all links
$(document).on('mousedown touchstart', 'a', show_overlay);
});
I made a JS Fiddle to show it more clearly.
Does someone know about any mistake here ? Is my logic wrong ?
The problem is caused by the way jQuery executes .click(). It is a shortcut for jQuery's trigger('click') method, and this is what the documentation says:
Although .trigger() simulates an event activation, complete with a synthesized event object, it does not perfectly replicate a naturally-occurring event.
The solution is to call the click method on the DOM element itself. It seems though that the MDN documentation announces the same limitations apply to that method:
The HTMLElement.click() method simulates a mouse click on an element.
However, the click() method will not initiate navigation on an <a> element.
...but in trying this with the current versions of Firefox, Chrome and Edge, the DOM click method does launch the <a> navigation (also when href has a http URL).
So, change this:
overlay_triggered = $target;
to:
overlay_triggered = evt.target;
And change this (which was overkill anyway):
$(overlay_triggered).click();
to:
overlay_triggered.click();
See the updated jsfiddle.
I am not exactly sure why this won't work but I got it working by changing $(overlay_triggered).click(); to window.location = $(overlay_triggered).attr("href");
I have slightly modified the JS part of yours, and all is okay now
The thing i did is holding the target's href value in a variable ( I stored the target's href value in 'href' vaiable in the code below ), and after that "window.location = href;" in 'hide_overlay' function does the trick.
<script>
$(document).ready(function(){
// the full-page overlay selector
var $overlay = $('.body-overlay');
// a flag to keep a trace of clicked element
var overlay_triggered = false;
var href = false;
// the "mousedown" event handler
function show_overlay(evt)
{
var $target = $(evt.target);
href = evt.target.href;
if (overlay_triggered === false) {
$overlay.show();
overlay_triggered = $target;
$(document).on('mouseup touchend', hide_overlay);
}
return true;
}
// the "mouseup" event handler
function hide_overlay(evt)
{
if (overlay_triggered !== false) {
$(document).off('mouseup touchend', hide_overlay);
$overlay.hide();
// !!! - here I try to trigger a classic click but it never works
if( href ) {
window.location = href;
}
overlay_triggered = false;
}
return true;
}
// attachment of the mousedown handler on all links
$(document).on('mousedown touchstart', 'a', show_overlay);
});
</script>

Prevent click on outer li while clicking on inner link

I have some lis including a links as below
<li>
<span>SomeText</span>
<a href='someurl' class='entityDetailModal'>sometext</a>
</li>
I am using a third party library ('LightGallery') that adds click event on Li, and by Jquery I have add click event to the links to show a dialog.
The problem is when I click on link both click event will be fired,
my click event handler is
$('body').on("click", 'a.entityDetailModal', function (event) {
event.preventDefault();
event.stopPropagation();
loadDialog(this, event, '#mainContainer', true, true, false);
return false;
});
I tried event.stopPropagation() and event.preventDefault(); and return false; in link onclick event handler but they don't work.
Sample:http://jsfiddle.net/HuKab/30/
How can I overcome this?
Update
It seems the problem is the way I add click event handler,
using this way it seems that everything is ok
$('a.entityDetailModal').click(function (event) {
event.preventDefault();
event.stopPropagation();
loadDialog(this, event, '#mainContainer', true, true, false);
return false;
});
Update 2
Thanks #Huangism, this post stackoverflow.com/questions/16492254/pros-and-cons-of-using-e-stoppropagation-to-prevent-event-bubbling is explaining the reason.
Use stopPropagation(); in child element
$("li").click(function (e) {
alert("li");
});
$("a").click(function (e) {
e.preventDefault(); // stop the default action if u need
e.stopPropagation();
alert("a");
});
DEMO
It is not very clear to me what your problem really is. If you simply want to get rid of the click on the li tag you may use .unbind() from jQuery (see: http://api.jquery.com/unbind/). You should end up with only your click event.
Another thing that might help is to use something like:
$("a").on('click.myContext', function(event) {
//Your action goes here
}
This way you can have parallel events and turn them on with $("a").on('click.myContext') and off with $("a").off('click.myContext')
Edit: Use:
$("a").on('click.myContext',function (e) {
e.stopPropagation();
alert("a");
});
see working example: http://jsfiddle.net/bGBLz/4/

click outside DIV

<body>
<div id="aaa">
<div id="bbb">
</div>
</div>
</body>
$(#?????).click(function(){
$('#bbb').hide();
})
http://jsfiddle.net/GkRY2/
What i must use if i want hide #bbb if user click outside box #bbb? But if i click on div #bbb then box is still visible - only outside.
$('body').click(function(e){
if( e.target.id == 'bbb' )
{ return true; }
else
{ $('#bbb').hide(); }
});
A note of explanation: There are a few ways to do this, either way we need to listen for a click on a parent element, weather it be a direct parent like #aaa or a distant parent like the body or the document. This way we can capture clicks that occur outside of #bbb.
Now that we have that we need the .hide to NOT occur if the user did click inside of #bbb. We can do this two ways
Stop propagation if the user clicks on #bbb. This will make the click event not 'bubble' up to the parent. That way the click event never reaches the parent and so #bbb will not hide. I personally don't like this method because stop propagation will so ALL click events from bubbling, and you may have click events that you would like to bubble to a local parent and not a distant parent. Or you may have listeners delegated from a distant parent, which will stop working if click propagation is stopped.
Check for the #bbb element in the parent listener. This is the method shown above. Basically this listens on a distant parent, and when a click occurs it checks to see if that click is on #bbb specifically. If it IS NOT on #bbb .hide is fired, otherwise it returns true, so other things that may be tied into the click event will continue working. I prefer this method for that reason alone, but secondarily its a-little bit more readable and understandable.
Finally the manner in which you check to see if the click originated at #bbb you have many options. Any will work, the pattern is the real meat of this thing.
http://jsfiddle.net/tpBq4/ //Modded from #Raminson who's answer is very similar.
New suggestion, leverage event bubbling without jQuery.
var isOutSide = true
bbb = documment.getElementById('bbb');
document.body.addEventListener('click', function(){
if(!isOutSide){
bbb.style.display = 'none';
}
isOutSide = true;
});
bbb.addEventListener('click', function(){
isOutSide = false;
});
Catch the click event as it bubbles-up to the document element. When it hits the document element, hide the element. Then in a click event handler for the element, stop the propagation of the event so it doesn't reach the document element:
$(function () {
$(document).on('click', function () {
$('#bbb').hide();
});
$('#bbb').on('click', function (event) {
event.stopPropagation();
});
});
Here is a demo: http://jsfiddle.net/KVXNL/
Docs for event.stopPropagation(): http://api.jquery.com/event.stopPropagation/
I made a plugin that does this. It preserves the value for this where as these other solutions' this value will refer to document.
https://github.com/tylercrompton/clickOut
Use:
$('#bbb').clickOut(function () {
$(this).hide();
});
You can use target property of the event object, try the following:
$(document).click(function(e) {
if (e.target.id != 'bbb') {
$('#bbb').hide();
}
})
DEMO
This will work
$("#aaa").click(function(){
$('#bbb').hide();
});
$("#bbb").click(function(event){
event.stopPropagation();
})​
Becouse bbb is inside the aaa the event will "bubbel up to aaa". So you have to stop the bubbling by using the event.stopPropagation when bbb is clicked
http://jsfiddle.net/GkRY2/5/
OK
* this is none jquery. you can easly modify it to work with IE
first create helper method to facilitate codding don't get confused with JQuery $()
function $g(element) {
return document.getElementById(element);
}
create our listener class
function globalClickEventListener(obj){
this.fire = function(event){
obj.onOutSideClick(event);
}
}
let's say we need to capture every click on document body
so we need to create listeners array and initialize our work. This method will be called on load
function initialize(){
// $g('body') will return reference to our document body. parameter 'body' is the id of our document body
$g('body').globalClickEventListeners = new Array();
$g('body').addGlobalClickEventListener = function (listener)
{
$g('body').globalClickEventListeners.push(listener);
}
// capture onclick event on document body and inform all listeners
$g('body').onclick = function(event) {
for(var i =0;i < $g('body').globalClickEventListeners.length; i++){
$g('body').globalClickEventListeners[i].fire(event);
}
}
}
after initialization we create event listener and pass reference of the object that needs to know every clcik on our document
function goListening(){
var icanSeeEveryClick = $g('myid');
var lsnr = new globalClickEventListener(icanSeeEveryClick);
// add our listener to listeners array
$g('body').addGlobalClickEventListener(lsnr);
// add event handling method to div
icanSeeEveryClick.onOutSideClick = function (event){
alert('Element with id : ' + event.target.id + ' has been clicked');
}
}
* Take into account the document body height and width
* Remove event listeners when you don't need them
$(document).click(function(event) {
if(!$(event.target).closest('#elementId').length) {
if($('#elementId').is(":visible")) {
$('#elementId').hide('fast');
}
}
})
Change the "#elementId" with your div.

Prevent keydown() from being captured by document binding

I'm not exactly sure how to phrase this, so I couldn't search it. Basically, I have a keydown() bind on $(document). I'd like to show() another div, and have all keydown events be rerouted to this div and prevented from firing off in the document handler. Is this even possible, or would I have to put all my main keybindings on another div and work from there?
e.stopPropagation, or
e.preventDefault (depending on the situation)
Where e is the event.
Ex:
function onKeyDown(e) {
doStuff();
e.preventDefault();
}
e.preventDefault() will prevent the default behaviour of an event. What you need is to use
e.stopPropagation(), so that the event does not bubble up the DOM structure.
$(element).keydown(function(e) {
// do the task
// allow the default behaviour also
e.stopPropagation();
//^. BUT stop the even bubbling up right here
});
e.stopProgation(), can be bit confusing to grasp on the first but I created a demo with click event to explain it.
Hope it helps!!
Try:
​$(document).on('keydown', function (evt) {
$('#foo').show().trigger(evt);
});​​​​​
$('#foo').on('keydown', function (evt) {
console.log(evt);
return false; // this is very important. Without it, you'll get an endless loop.
});
​
demo: http://jsfiddle.net/Z7vYK/
The only way I can think of to even have a keydown event run on something other than an input or document, is to manually trigger it. You could have a global variable keep track of whether or not your div is showing, then trigger the event on your div accordingly.
Here's one such solution
HTML
Show div
<div id="hiddendiv"></div>​
Javascript
var showing = false;
function showdiv()
{
showing = true;
$('#hiddendiv').show(200);
}
// Set up events on page ready
$(function() {
$(document).keydown(function(e) {
// If the div is showing, trigger it's keydown
// event and return
if(showing)
{
$('#hiddendiv').data('keydown_event', e).keydown();
return true;
}
alert('Document keydown! Keycode: ' + e.keyCode);
// Otherwise do the normal keydown stuff...
});
// Keydown for the hidden div
$('#hiddendiv').keydown(function() {
e = $(this).data('keydown_event');
alert('Hiddendiv keydown! Keycode: ' + e.keyCode);
// Make sure to stop propagation, or the events
// will loop for ever
e.stopPropagation();
return false;
});
});
​
As you can see, the #hiddendiv keydown event is being triggered by the document keydown event. I've also included a slight hack to get the event object to the hidden div using the jQuery data function.
Here's a demonstration of the code: http://jsfiddle.net/Codemonkey/DZecX/1/

Categories

Resources