Jquery long press, stop short press action not working - javascript

I am trying to implement one event for a short press and a different for a long press. The short press is just doing the default action. The long press works, but also does the default action still. What am I missing?
HTML
<"Label for my Link"
Javascript
$(document).ready(function(){
$('.recordlongpress').each(function() {
var timeout, longtouch;
$(this).mousedown(function() {
timeout = setTimeout(function() {
longtouch = true;
}, 1000);
}).mouseup(function(e) {
if (longtouch) {
e.preventDefault();
$('#popupPanel').popup("open");
return false;
} else {
return;
}
longtouch = false;
clearTimeout(timeout);
});
});
});
I followed the jQuery documentation and was under the impress "preventDefault" should stop the short press default action. Any examples I have found online do not seem to be exactly my situation. I appreciate you taking the time to read this. Thank you for any input.

You're returning from your "mouseup" handler before clearing the timeout and setting "longtouch" to false.
Try:
}).mouseup(function(e) {
var returnval;
if (longtouch) {
e.preventDefault();
$('#popupPanel').popup("open");
returnval = false;
}
longtouch = false;
clearTimeout(timeout);
return returnVal;
});
I'd also clear "longtouch" in the "mousedown" handler. That said, I wouldn't do this with mouse events. I'd use "touchstart" and "touchend". On touch screen devices, "mouse" events are simulated from touch events, and there's a distinct delay involved. (You may also want to detect whether the finger moved during the touch period.)

jsFiddle Demo
In your code these lines are unreachable
longtouch = false;
clearTimeout(timeout);
JS:
$('.recordlongpress').each(function () {
var timeout, longtouch = false;
$(this).mousedown(function () {
timeout = setTimeout(function () {
longtouch = true;
}, 1000);
e.preventDefault();
}).mouseup(function (e) {
clearTimeout(timeout);
if (longtouch == true) {
longtouch = false;
$('body').append("long press" + longtouch);
return false;
} else {
return;
}
});
});

#Pointy lead me towards a working solution for clicking events.
$(document).ready(function(){
$('.recordlongpress').bind('tap', function(event) {
return;
});
$('.recordlongpress').bind('taphold', function(event) {
$('#popupPanel').popup("open");
});
});
Something still needs to be added because upon a long press on my mobile device, the default options screen with the four options; open, save link, copy link URL and select text still pops up as well. I will add on the fix for that once I find it.

Related

Temporarily disable touchstart event

I have a mobile based web application. Currently I am encountering an issue when ajax calls are being made. The wait spinner which is enclosed in a div can be clicked through on the ipad device. The javascript event being triggered is touchstart. Is there anyway to prevent this event from going through normal processing?
Tried to call the following, however it did not work.
Disable
document.ontouchstart = function(e){ e.preventDefault(); }
Enable
document.ontouchstart = function(e){ return true; }
How touchstart is handled
$(document).on('touchstart', function (eventObj) {
//toggle for view-icon
if (eventObj.target.id == "view-icon") {
$("#view-dropdown").toggle();
} else if ($(eventObj.target).hasClass("view-dropdown")) {
$("#view-dropdown").show();
} else {
$("#view-dropdown").hide();
}
});
As user3032973 commented, you can use a touchLocked variable, which is working perfectly.
I have used it in combination with the Cordova Keyboard-Plugin. Scrolling will be disabled the time the keyboard is shown up and reenabled the time the keyboard is hiding:
var touchLocked = false;
Keyboard.onshowing = function () {
touchLocked = true;
};
Keyboard.onhiding = function () {
touchLocked = false;
};
document.ontouchstart = function(e){
if(touchLocked){
e.preventDefault();
}
};

jQuery slideToggle menus based on mouseenter/leave

I've mostly got a little menu system working, but having a couple quirks I can't figure out. There don't seem to be any questions I can find with this same issue.
The functional example is at http://louisnk.com/photography - the only menu that has pretty much all the code in place is 'USA'.
on click, the menu shows, and I want it to:
A) delay, then slide back up if the mouseenter event never fires
B) not slide back up if the mouseenter event does fire
C) delay, then slide back up when the mouseleaves
I have it pretty well down, except I believe I have some event delegation issues...
The first time I click, it all works great. The second time I click on any menu, it seems like 'poked' (the variable which gets set as true/false depending on the switch case) is being set to false, and so automatically causing the 'noPoke' function to close the menu regardless of the mouseenter event. Subsequent clicks obviously cause stranger behavior.
I have the following code all wrapped in a document ready function:
var menuSwitch = function(menu) {
$(menu).on('mouseenter mouseleave', function(e) {
switch(e.type) {
case 'mouseenter':
poked = true;
console.log('probed');
return false;
break;
case 'mouseleave':
$(this).delay(1500).slideUp(500,function() {
$(menu + 'li').hide();
console.log('switched');
});
poked = false;
break;
default:
poked = false;
console.log('defaulting');
break;
}
});
}
var noPoke = function(menu) {
if (poked == false) {
$(menu).delay(2000).slideUp(500,function() {
$(menu + 'ul').hide();
console.log('no pokes given');
});
poked = true;
}
}
var poked = false;
$('.usa').click(function(e) {
e.preventDefault();
poked = false;
$('.usaMenu').slideToggle(500);
menuSwitch('.usaMenu');
console.log(poked);
$('.usaMenu li').on('mouseenter', function() {
if ($(this).children() != false) {
$(this).children().fadeTo(200,1);
}
else {
$('.usaMenu li>ul').fadeTo(200,0).hide();
}
});
noPoke('.usaMenu');
});
So, after spending another 6 hours researching and trying different things, I ended up with the following, which solved the problems I was having.
Hopefully this is useful to people trying to do this in the future.
var menuSwitch = function(menu) {
if ($(menu).is(':hover') === false) {
var hideMe = setTimeout(function() {
$(menu).slideToggle(500);
},4000);
}
$(menu).on('mouseenter mouseleave', function(e) {
switch(e.type) {
case 'mouseenter':
console.log('probed');
clearTimeout(hideMe);
$(this).stop();
$(this).slideDown(200);
break;
case 'mouseleave':
$(this).delay(2000).slideUp(500,function() {
$('li>ul',menu).hide();
console.log('switched');
});
break;
}
});
}
var babies = function(menu) {
$('li',menu).unbind('mouseenter mouseleave click');
$('li',menu).each(function() {
if ($('li',menu).children() != false) {
$(this).on('click',function(e) {
e.preventDefault();
$(this).children('ul').slideToggle(300);
});
}
});
}
var menuCtrl = function(menu) {
$(menu).slideToggle(500);
$(menu).unbind('mouseenter mouseleave');
menuSwitch(menu);
babies(menu);
}
$('.usa').click(function(e) {
e.preventDefault();
menuCtrl('.usaMenu');
});
Ultimately it turned out that because the initial click handler was running the functions every time it fired, it was creating duplicates of the mouseenter/leave event handlers, and therefore causing all sorts of problems. I was also trying to make the menu close if the mouse never entered, in the wrong way.
To solve the most obnoxious issue (duplicate event handlers), I simply used the unbind() api, passing it the events to unbind at the start of the function, so that when they were re-bound, there was only one instance. This may not be the most efficient way, but it works.
To solve the timing/hiding if no mouseenter issue, I used a setTimeout function, which was then cleared if mouseenter-ed.

How to apply long click event and doubleclick event on the same element in javascript

I have an element(textArea). Now I would like a long press event and a double click event on the element. I am able to do this but I would also like to use event.preventDefault() in the mousedown event of long press event. This in turn prevents the dblClick event also.
The reason why I want to preventDefault is I am rendering an element on longPress and wanted to prevent the initial mouseDown as I am firing mousemove after longpress. I have searched and re-searched the net but am unable to find a good answer which solves the problem of long press and dblclick on the same element.
thanks!!
try this Demo
HTML
<input type="button" ondblclick="whateverFunc()" onmousedown="func(event)" onmouseup="revert()" value="hold for long"/>
JavaScript
var timer;
var istrue = false;
var delay = 3000; // how much long u have to hold click in MS
function func(e)
{
istrue = true;
timer = setTimeout(function(){ makeChange();},delay);
// Incase if you want to prevent Default functionality on mouse down
if (e.preventDefault)
{
e.preventDefault();
} else {
e.returnValue = false;
}
}
function makeChange()
{
if(timer)
clearTimeout(timer);
if(istrue)
{
/// rest of your code
alert('holding');
}
}
function revert()
{
istrue =false;
}
function whateverFunc()
{
alert('dblclick');
}

how to detect if a link was clicked when window.onbeforeunload is triggered?

I have window.onbeforeunload triggering properly. It's displaying a confirmation box to ensure the user knows they are navigating (closing) the window and that any unsaved work will be erased.
I have a unique situation where I don't want this to trigger if a user navigates away from the page by clicking a link, but I can't figure out how to detect if a link has been clicked inside the function to halt the function. This is what I have for code:
window.onbeforeunload = function() {
var message = 'You are leaving the page.';
/* If this is Firefox */
if(/Firefox[\/\s](\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 4) {
if(confirm(message)) {
history.go();
}
else {
window.setTimeout(function() {
window.stop();
}, 1);
}
}
/* Everything else */
else {
return message;
}
}
You're looking for deferred event handling. I'll explain using jQuery, as it is less code:
window._link_was_clicked = false;
window.onbeforeunload = function(event) {
if (window._link_was_clicked) {
return; // abort beforeunload
}
// your event handling
};
jQuery(document).on('click', 'a', function(event) {
window._link_was_clicked = true;
});
a (very) poor man's implementation without jQuery's convenient delegation handling could look like:
document.addEventListener("click", function(event) {
if (this.nodeName.toLowerCase() === 'a') {
window._link_was_clicked = true;
}
}, true);
this allows all links on your page to leave without invoking the beforeunload handler. I'm sure you can figure out how to customize this, should you only want to allow this for a specific set of links (your question wasn't particularly clear on that).
var link_was_clicked = false;
document.addEventListener("click", function(e) {
if (e.target.nodeName.toLowerCase() === 'a') {
link_was_clicked = true;
}
}, true);
window.onbeforeunload = function() {
if(link_was_clicked) {
link_was_clicked = false;
return;
}
//other code here
}
You can differ between a link unload or a reload/user entering a different address unload s by using a timer. This way you know the beforeunload was triggered directly after the link click.
Example using jQuery:
$('a').on('click', function(){
window.last_clicked_time = new Date().getTime();
window.last_clicked = $(this);
});
$(window).bind('beforeunload', function() {
var time_now = new Date().getTime();
var link_clicked = window.last_clicked != undefined;
var within_click_offset = (time_now - window.last_clicked_time) < 100;
if (link_clicked && within_click_offset) {
return 'You clicked a link to '+window.last_clicked[0].href+'!';
} else {
return 'You are leaving or reloading the page!';
}
});
(tested in Chrome)

Prevent click event from firing when dblclick event fires

I'm handling both the click and dblclick event on a DOM element. Each one carries out a different command, but I find that when double clicking on the element, in addition to firing the double click event, the click event is also fired twice. What is the best approach for preventing this behavior?
In case anyone else stumbles on this (as I did) looking for an answer, the absolute best solution that I could come up with is the following:
$node.on('click',function(e){
if(e.originalEvent.detail > 1){
return;
/* if you are returning a value from this
function then return false or cancel
the event some other way */
}
});
Done. If there is more than one click back to back, the second, third,etc. will not fire. I definitely prefer this to using any sort of timers.
I got myself pointed in this direction by reading this.
Incidentally: I was first researching this problem because I accidentally double clicked a paginated link, and the event fired and finished twice before the callback could happen.
Before coming up with the code above, I had
if e.originalEvent.detail === 2 //return
however, I was able to click on the link 3 times (a triple click), and though the second click didn't fire, the third did
In a comment, you said,
I delay the click handler by 300 ms (a noticeable and annoying delay) and even ...
So it sounds like what you want is that when you click then the DOM should geneate a click event immediately, except not if the click is the first click of a double-click.
To implement this feature, when you click, the DOM would need to be able to predict whether this is the final click or whether it's the first of a double-click (however I don't think is possible in general for the DOM to predict whether the user is about to click again).
What are the two distinct actions which you're trying to take on click and double-click? IMO, in a normal application you might want both events: e.g. single-click to focus on an element and then double-click to activate it.
When you must separate the events, some applications use something other than double-click: for example, they use right-click, or control-click.
You can use UIEvent.detail if you want to detect how many times the element was clicked and fire events based on that.
A simple example:
element.addEventListener("click", function (e) {
if (e.detail === 1) {
// do something if the element was clicked once.
} else if (e.detail === 2) {
// do something else if the element was clicked twice
}
});
In this case, it is best to delay the execution of the single click event slightly. Have your double click handler set a variable that the single click event will check. If that variable has a particular value, could be boolDoubleClick == true, then don't fire/handle the single click.
Thanks to all the other answers here as the combination of them seems to provide a reasonable solution for me when the interaction requires both, but mutually exclusive:
var pendingClick = 0;
function xorClick(e) {
// kill any pending single clicks
if (pendingClick) {
clearTimeout(pendingClick);
pendingClick = 0;
}
switch (e.detail) {
case 1:
pendingClick = setTimeout(function() {
console.log('single click action here');
}, 500);// should match OS multi-click speed
break;
case 2:
console.log('double click action here');
break;
default:
console.log('higher multi-click actions can be added as needed');
break;
}
}
myElem.addEventListener('click', xorClick, false);
Update: I added a generalized version of this approach along with a click polyfill for touch devices to this Github repo with examples:
https://github.com/mckamey/doubleTap.js
AFAIK DOM Level 2 Events makes no specification for double-click.
It doesn't work for me on IE7 (there's a shock), but FF and Opera have no problem managing the spec, where I can attach all actions to the click event, but for double-click just wait till the "detail" attribute of the event object is 2. From the docs: "If multiple clicks occur at the same screen location, the sequence repeats with the detail attribute incrementing with each repetition."
Here is what I did to distinguish within a module
node.on('click', function(e) {
//Prepare for double click, continue to clickHandler doesn't come soon enough
console.log("cleared timeout in click",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
_this.clickTimeout = setTimeout(function(){
console.log("handling click");
_this.onClick(e);
},200);
console.log(_this.clickTimeout);
});
node.on('dblclick', function (e) {
console.log("cleared timeout in dblclick",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
// Rest of the handler function
I use this solution for my project to prevent click event action, if I had dblclick event that should do different thing.
Note: this solution is just for click and dblclick and not any other thing like tripleclick or etc.
To see proper time between click and double click see this
sorry for my bad English.
I hope it helps :)
var button, isDblclick, timeoutTiming;
var clickTimeout, dblclickTimeout;
//-----
button = $('#button');
isDblclick = false;
/*
the proper time between click and dblclick is not standardized,
and is cutsomizable by user apparently (but this is windows standard I guess!)
*/
timeoutTiming = 500;
//-----
button.on('dblclick', function () {
isDblclick = true;
clearTimeout(dblclickTimeout);
dblclickTimeout = setTimeout(function () {
isDblclick = false;
}, timeoutTiming);
//-----
// here goes your dblclick codes
console.log('double clicked! not click.');
}).on('click', function () {
clearTimeout(clickTimeout);
clickTimeout = setTimeout(function () {
if(!isDblclick) {
// here goes your click codes
console.log('a simple click.');
}
}, timeoutTiming);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" id="button">
click/dblclick on this to see the result
</button>
It can be achieved via following code
var clickHandler = function(e) { /* put click event handling code here */ };
var doubleclickHandler = function(e) { /* put doubleclick event handling code here */ }
const maxMsBetweenClicks = 300;
var clickTimeoutId = null;
document.addEventListener("dblclick", handleDoubleClick);
document.addEventListener("click", handleSingleClick);
function handleSingleClick(e){
clearTimeout(clickTimeoutId);
clickTimeoutId = setTimeout( function() { clickHandler(e);}, maxMsBetweenClicks);
}
function handleDoubleClick(e){
clearTimeout(clickTimeoutId);
doubleclickHandler(e);
}
I know this is old as heck, but thought I'd post anyhow since I just ran into the same problem. Here's how I resolved it.
$('#alerts-display, #object-display').on('click', ['.item-data-summary', '.item-marker'], function(e) {
e.preventDefault();
var id;
id = setTimeout(() => {
// code to run here
return false;
}, 150);
timeoutIDForDoubleClick.push(id);
});
$('.panel-items-set-marker-view').on('dblclick', ['.summary', '.marker'], function(e) {
for (let i = 0; i < timeoutIDForDoubleClick.length; i++) {
clearTimeout(timeoutIDForDoubleClick[i]);
}
// code to run on double click
e.preventDefault();
});
Here is my simple solution to prevent the second click. Of course, I could restart the timeout when a double click detected, but in reality I never need it.
clickTimeoutId = null;
onClick(e) {
if (clickTimeoutId !== null) {
// Double click, do nothing
return;
}
// Single click
// TODO smth
clickTimeoutId = setTimeout(() => {
clearTimeout(clickTimeoutId);
clickTimeoutId = null;
}, 300);
}
Summarizing, to recognize the simpleClick and doubleClick events on the same element, just treat the onClick event with this method:
var EVENT_DOUBLE_CLICK_DELAY = 220; // Adjust max delay btw two clicks (ms)
var eventClickPending = 0;
function onClick(e){
if ((e.detail == 2 ) && (eventClickPending!= 0)) {
// console.log('double click action here ' + e.detail);
clearTimeout(eventClickPending);
eventClickPending = 0;
// call your double click method
fncEventDblclick(e);
} else if ((e.detail === 1 ) && (eventClickPending== 0)){
// console.log('sigle click action here 1');
eventClickPending= setTimeout(function() {
// console.log('Executing sigle click');
eventClickPending = 0
// call your single click method
fncEventClick(e);
}, EVENT_DOUBLE_CLICK_DELAY);
// } else { // do nothing
// console.log('more than two clicks action here ' + e.detail);
}
}
You can use debounce to free the single click handler from detecting the double/multiple clicks
Test at: https://jsfiddle.net/L3sajybp/
HTML
<div id='toDetect'>
Click or double-click me
</div>
<hr/>
<ol id='info'>
</ol>
JS
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this,
args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
function debounceSingleClickOnly(func, timeout = 500) {
function eventHandler (event) {
const { detail } = event;
if (detail > 1) {
console.log('no double click for you '+ func.name);
console.log('');
return;
}
func.apply(this, arguments);
}
return debounce(eventHandler, timeout);
}
window.toDetect.addEventListener('click', debounceSingleClickOnly(handleSingleClick));
window.toDetect.addEventListener('dblclick', handleDoubleClick);
function handleS() {
console.log('S func');
console.log(this.id);
}
function handleSingleClick(event) {
console.log('single click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('single click'));
window.info.appendChild(divText)
console.group();
console.log('this element was single-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
function handleDoubleClick(event) {
console.log('double click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('double click'));
window.info.appendChild(divText);
console.group();
console.log('this element was double-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
Output:
const toggle = () => {
watchDouble += 1;
setTimeout(()=>{
if (watchDouble === 2) {
console.log('double' + watchDouble)
} else if (watchDouble === 1) {
console.log("signle" + watchDouble)
}
watchDouble = 0
},200);
}

Categories