HammerJS triggers click after dragend - javascript

I have a perfectly working hammerjs drag listener that captures drags just like I'd expect. But when you let go, right after the dragend is triggered, it then triggers a click event as well. So if you were clicking to drag (move) the target, and the cursor happened to be over a link, when you let go (drop), it triggers a click on the link, which I don't want.
Here's my code:
$('.draggable').hammer({
drag_min_distance: 0,
drag_max_touches: 2,
drag_lock_to_axis: true,
drag_lock_min_distance: 30
}).on('drag', handleMiddleEvent)
.on('dragstart', handleStartEvent)
.on('dragend', handleEndEvent);
If I console.log the event on all three handlers and console.log click events on all links, I get something like this in my console:
dragstart
dragmiddle
dragmiddle
dragmiddle
dragmiddle
dragend
click
I want to avoid the click at the end. I tried adding event.stopPropagation() to all of the handle methods, but to no avail.

Try using Hammer.js tap events instead of the click events on the links.

I had a pretty similar problem, and found some kind of workaround - I know it's dirty but the links are not being clicked by a drag. I want to drag a wrapper-div, which is my wrapper element seen in the js below. It has several links in it, wich are being clicked when the dragging stops above them, and i want to prevent that from happening.
First, i define a div that is on top of everything but set hidden in my html:
<div id="wrapper">
<div id="linklist">
<-- Links etc in here-->
<div id="preventClick" style="visibility: hidden; position: absolute; width: 5000px; height: 6000px; "></div>
</div>
</div>
Then, in the hammer functions, the div is set to visible at a dragstart (in this case, a dragdown) - you just can't see it because it has no background-color or any content.
JS:
var wrapper = document.getElementById("wrapper");
Hammer(wrapper).on("dragdown", function() {
handleDragDown(); //call function that handles movement
var preventClick = document.getElementById("preventClick");
preventClick.style.visibility = 'visible';
});
Hammer(wrapper).on("dragend", function() {
var preventClick = document.getElementById("preventClick");
preventClick.style.visibility = 'hidden';
});
I know this is far from perfect and your post is from several months ago, but it worked for me. Hope i could help :)

Related

Enlarge img on click, click again to minimize

I'm making a website which requires pictures to be clickable. If you click on the image it should enlarge and show in the middle of the screen. If you then click again it should go smaller again and back on its place.
$(document).ready(function() {
$("#header").load("header.html .header");
$("#footer").load("footer.html .footer");
$("body").on('click', function(){
if(!$(".img1, .img2").hasClass('enlarged')){
$(".img1, .img2").on('click',function(){
$(this).addClass('enlarged');
});
}else{
$("body").on('click', '.enlarged', function(){
$(this).removeClass('enlarged');
});
}
});
});
.enlarged{
position:absolute;
z-index:2;
width:500px;
height:600px;
top:-10%;
left:300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container" class="container">
<aside class="aside"><img src="fotos/foto1.JPG" id="img1" class="img1" style="transform:rotate(90deg);"/><img src="fotos/foto2.JPG" class="img2" style="transform:rotate(90deg);"/></aside>
<div class="box"></div>
</div>
My current script works but is very wonky. It only enlarges once and you have to triple-click.
I already made a question about it before, but after I updated nobody answered.
Also I'm not sure how to add images on Stack Overflow, otherwise I would've made a snippet.
Your click handler isn't actually performing the logic you want, it's just assigning other click handlers. Then upon further clicks those are performing the logic you want (sort of), but also further assigning more click handlers. After a couple clicks, this is going to get entirely weird.
You just want one click handler for your target elements:
$("body").on('click', '.img1, .img2', function(){
});
This handler would be invoked for any .img1 or .img2 on the page. Inside this handler, conduct your logic:
if (!$(this).hasClass('enlarged')) {
$(this).addClass('enlarged');
} else {
$(this).removeClass('enlarged');
}
Or, even simpler:
$(this).toggleClass('enlarged');
A lot of things are happening in your code:
You first only add a click handler on the body element. Which means the 1st time you click on the body it enters your conditional. In your first if you add another click handler, but now on .img1, .img2. The opposite (removing the enlarged class) is again on the body element and not on the .img1, .img2 elements.
Some advice:
Use only 1 class to toggle the enlarged state
Personally I'm a big fan of BEM, maybe not relevant for now.
Split up your JS concerns; I would create a plugin for this behaviour
A bit of code to point you in the right direction:
var $elements = $('.enlarge-img');
// in this case not very relevant, but a good habit to have
// a method to instantiate
function initialize() {
addEventListeners();
}
// adding event listeners
function addEventListeners() {
$elements.on('click.namespace', handleClick);
$('body').click('click.namespace', handleClickBody);
}
// You might want to handle clicks on the <body> to remove
// the enlarged state of any image
function handleClickBody(event) {
$elements.removeClass('enlarge-img--enlarged');
}
// If you click an image, you want to toggle the enlarged state class
function handleClick(event) {
event.preventDefault(); // not necessary on a <img/> but needed if you use a link
$(event.target).toggleClass('enlarge-img--enlarged`);
}
initialize();
Hopefully this helps you in the right direction of creating re-usable code. Happy coding!

getting a mouseenter event from a still mouse entering an animated element

I'm writing a image carousel and due to some class adding/removing my css pointer as well as my mouseenter event don't seem to work properly.
$("img", ":not(.active)").on("click", function() {
var $this = $(this);
$("img").removeClass("active");
$this.addClass("active");
goto($this.index());
});
$("img").on("mouseenter", function() {
console.log("silence");
});
function goto(i) {
$(".images").animate({
left: 55-i*310
});
}
http://jsfiddle.net/rnfkqq6s/3/
please take a look at the fiddle and watch the console. when the mouse doesn't move while clicking, the mouseenter sometimes isn't beeing triggered. the same thing with the cursor. what am I doing wrong here?
This issue relates to a known bug:
See similar:
Getting the browser cursor from "wait" to "auto" without the user moving the mouse
The bug report:
https://code.google.com/p/chromium/issues/detail?id=26723#c87

Strange lag which disappears by clicking anywhere on the html body

I have some overlay elements which are display: none initially but turn to display: inline when I hover over specific items on the page, and disappear again when the mouse hovers over something else. Exactly same behavior as tool-tips with the difference that this overlay objects have clickable and interactive elements (such as a jquery accordion).
Everything works perfectly, until I interact with these overlay elements, i.e. click on one of the clickable items in the overlay element. Then, once that overlay item becomes display:none again, the page becomes extremely laggy in terms of how long it takes when I hover over an item to find its corresponding overlay element (they are selected by their id) and for it to appear and disappear.
The strange thing is that if I click anywhere on the html body, the lag disappears and everything becomes fast as in the beginning.
Out of despair, I have tried to programmatically call blur, focus, trigger('click') once the overlay element is set back to display:none but none has helped so far, and I have to manually click on the page for the lag to go away.
Any idea what causes such behavior and how I can fix it? thanks,
Edit: code
CSS part:
span.overlay {
z-index:10;
display:none;
position:absolute;
}
span.visible { display:inline; }
HTML part: lots of such span elements, each with their own unique id.
<span class='overlay ui-widget-content' id='xyz'>
<!-- lots of stuff here -->
</span>
javascript part:
/* displays overlay element when user hovers over the first td */
$('table.foo > tbody > tr > td:first-child').hover(
function(e) {
$(this).parent().tooltip('disable');
var elem = $('#' + $(this).parent().data('overlay-id'));
if (!elem.hasClass('visible')) {
elem.css('left', e.pageX + 20).css('top', e.pageY).addClass('visible');
elem.find('.accordion:first').accordion('refresh');
}
}, function() {
var elem = $('#' + $(this).parent().data('overlay-id'));
if (! elem.is(':hover') && ! elem.hasClass('pin')) {
$(elem).removeClass('visible');
}
$(this).parent().tooltip('enable');
});
/* if mouse leaves span.visible and it is not pinned it will hide the span */
$('body').on('mouseleave', 'span.visible',
function() {
if (!$(this).hasClass('pin')) {
$(this).removeClass('visible');
}
});
Edit: profiling the code, it seems that get offsetHeight and get offsetWidth take way longer than before. Yet I do not know why this should happen and why it should go away by clicking on the page.
previously, when I do not observe the problem, these two functions each take less than 3%.
try binding the mouseleave event upon opening the "tooltip". Replace your code with this (not tested):
/* displays overlay element when user hovers over the first td */
$('table.foo > tbody > tr > td:first-child').on('mouseenter',
function(e) {
$(this).parent().tooltip('disable');
var elem = $('#' + $(this).parent().data('overlay-id'));
if (!elem.hasClass('visible')) {
elem.css('left', e.pageX + 20).css('top', e.pageY).addClass('visible');
elem.find('.accordion:first').accordion('refresh');
// notice the "ONE" handler, it'll unbind the event after execution
elem.one('mouseleave', function() {
if (!$(this).hasClass('pin')) {
$(this).removeClass('visible');
}
$(this).parent().tooltip('enable');
});
}
}
);
Notice the one listener to unbind the event after it's first execution.
I can't guarantee that this will fix your issue but I experienced lots of performance hits when a page has A LOT of elements and browsers need to check hover events that change very quickly.
This way the browser only needs to check one mouseleave event. And if it happened, it's gone again. It seems you may have too many bound events and don't clean them up properly.
I'm not sure if I replicated your desired functionality correctly so please add code if I missed something. I was unsure why exactly you'd need to bind a mouseleave event via body AND via .hover().

mousedown Events in RaphaelJS not working

I have run into the problem that RaphaelJS doesn't react to mousedown/mousemove/mouseup Events but works fine with .click().
I created this http://jsfiddle.net/JMu7Z/2/ to show what i mean.
JS code:
var containerDivs = document.getElementsByClassName('container');
var overlayDiv = null;
for(var k=0;k<containerDivs[0].childNodes.length;k++)
{
if (containerDivs[0].childNodes[k].className.indexOf("holder") !== -1)
overlayDiv = containerDivs[0].childNodes[k];
}
var canvas = Raphael(overlayDiv,208,270);
var bgr = canvas.rect(10,10, canvas.width-10, canvas.height-10).attr({fill: "0xFF0000", stroke: "none", opacity:"0.2"});
bgr.mousedown( function(e) { alert ("down"); }); //doesn't work
bgr.click( function(e) { alert ("click"); }); // works
Html:
<div class="container" style="position: relative; left: 0; top: 0;"><img class="corePic nonselectable" style="position:relative; top: 0; left: 0;" src="http://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Tesla3.jpg/220px-Tesla3.jpg" alt="2_3"><div class="holder nonselectable" style="position:absolute; top:0px; left:0px;" onselectstart="return false;"></div></div>
Any help is highly appreciated.
Look at the DEMO I provided based on you example.
//bgr.click(function(){this.attr({stroke:'orange',"stroke-width":5,opacity:0.5});
bgr.mousedown(function()
{
this.attr({stroke:'red',"stroke-width":5,opacity:0.5});
});
bgr.mouseup(function()
{
this.attr({stroke:'blue',"stroke-width":5,opacity:0.5});
});
//bgr.mousemove(function(){this.attr({stroke:'green',"stroke-width":5,opacity:0.5});
Take note that there are some listeners that cannot be used simultaneously.
For example:
click and mousedown events should not be used together on the same object. Why? Because the mosedown event triggers as soon as you push mouse button down; whereas click is different. If you look at the DEMO, comment everything and uncomment click. When you press and hold the mouse button nothing happens until you release the button. That is why when you have both events on the same object, mousedown triggers first and takes over the click.
mouseup and mousemove also act similar to the previous point. mousemove triggers right away and takes over 'mouseup' event. That is why you can only see mousemove event on the object that has both events implemented.
Plus, mousemove is really annoying. When you have it, it will not even let you click, mousedown, or mouseup events from happening. In all three cases it triggers and stays on the whole time your mouse is over the element.
Hope this helped a little.

Internet Explorer and <select> tag problem

I am having the following problem under Internet Explorer 7/8:
I have a popup that gets activated when user mouseover a link. The popup is a simple <div> that contains some data. Inside this <div> tag there is a <select> tag with some <option>s. I have attached mouseover/mouseout events to the <div>, so that this popup will stay open while cursor is over it. The problem comes when you click on the <select> and then move the cursor over any of the <option>s. This triggers the mouseout event of the <div> tag and respectively closes it.
How can I prevent the closing of the popup in IE ?
You should be able to detect if the situation is the one you want just with the values off the event. It is a little convoluted but it seems to work.
In the event handler of your outer div, do something like this:
<div onmouseover="if (isReal()) { toggle(); }"
onmouseout="if (isReal()) { toggle(); }">
</div>
Then implement the isReal method:
function isReal() {
var evt = window.event;
if (!evt) {
return true;
}
var el;
if (evt.type === "mouseout") {
el = evt.toElement;
} else if (evt.type === "mouseover") {
el = evt.fromElement;
}
if (!el) {
return false;
}
while (el) {
if (el === evt.srcElement) {
return false;
}
el = el.parentNode;
}
return true;
}
Basically the isReal method just detects if the event was coming from within the div. If so, then it returns false which avoids calling the hide toggle.
My suggestion would be to set another flag while the select box has focus. Do not close the div while the flag is set.
How about re-showing the div when the mouse is over the <options>s through mouseover events of <options>s.
Edit: execution order of mouseover of option and mouseout of div might cause problems though.
In the mouseout event for the div add a timeout to the div element that will hide the div in 200 milliseconds or so.
Then in the mouseover event for the div/select and the click event of the select clear the timeout stored in the div element.
This gives a very slight delay before hiding the div that allows the mouseover or click events to clear the timeout before it is executed. It's not pretty but it should work.
instead of using mouseout as the event to close the div, use mouseleave, then the event will only be triggered when the pointer leaves the boundary of the div, not when it moves onto other elements within it
you could try adding another mouseover event specifically for the options list.
Well, the reason for this behavior is because the mouseover/out events bubble, which effectively means that whenever you mouseover any of the elements inside the popup, the popup receives the event also.
You can read more here about these events, and here about event bubbling.
You have 3 possible solutions here:
Change the events to onmouseenter/leave. You've mentioned that this didn't help, which just sounds plain odd, since these aren't supposed to bubble.
Check srcElement in relation to from/toElement in the event.
An improved version of McKAMEY's check would be:
function isReal() {
var evt = window.event;
if (!evt) {
return true;
}
var el;
if (evt.type === "mouseout") {
el = evt.toElement;
} else if (evt.type === "mouseover") {
el = evt.fromElement;
}
if (!el) {
return false;
}
// this will also return true if el == evt.srcElement
return evt.srcElement.contains(el);
}
Does the same thing, just shorter.
3 . Another option would be to create a transparent, invisible div just under your popup that covers the area that the select box drops down into. I'm assuming that it's dropping outside the actual area of the popup.
Hope this helps!
have you tried hover instead of mouseover/out effects?
$(".myDiv").hover(function(){
$(this).show();
}, function {
$(this).hide();
});
What about something like this:
<div id="trigger">
Hover over me!
</div>
<div class="container">
<select>
<option>Blah</option>
<option>Blah</option>
</select>
</div>
$("#trigger").mouseover(function(){
$('.container).show();
});
$(".container").mouseleave(function(){
$(this).hide();
});
The basic idea is that you show the container element when you hover over the trigger then when you leave the container you hide the container. You'd need to position the container so it clipped the trigger element, otherwise it would hide straight away.
Why have mouseover / mouseout on the <div>? Why not just show the <div> on the mouse over, then set <body onmouseover="hidedivs();"> I don't know if this would work, but if the <div> is on top of the body, then the <div> should stay visible.
Many people posting solutions/examples do not seem to realize one thing: onmouseout event on <div> fires before onmouseover event on <select>.
When <div> loses focus (onmouseout) do not close it immediately, but after say, 500 milliseconds. If during this time <select> gets focus (mouseover) do not close <div> at all (clearTimeout).
Also, try to play with event propagation/bubling.
Given that selects in IE are a pain, especially when it comes to the whole layering issue where a select appears above a div even though it shouldn't, can I point you in the direction of YUI's Menu button controls. They look really nice, are easy to implement and won't cause this issue
Here is a link: http://developer.yahoo.com/yui/examples/button/btn_example07.html
You should use event.stopPropagation() while in <select>, or cancelBubble() in <select> element itself.

Categories

Resources