For mobile devices there is not action like right-clicking - so I want to handle a long press for this.
I also need a normal "click" event.
For the long-press handling I found a solution, but when I add an onClick-listener, the onClick gets fired even if I only want the long-press event to be fired.
How can I prevent the Click Event when the longTap event fires?
Here is the Code + example:
var c = console.log.bind(console);
(function() {
$.fn.longTap = function(options) {
options = $.extend({
delay: 1000,
onRelease: null
}, options);
var eventType = {
mousedown: 'ontouchstart' in window ? 'touchstart' : 'mousedown',
mouseup: 'ontouchend' in window ? 'touchend' : 'mouseup'
};
return this.each(function() {
$(this).on(eventType.mousedown + '.longtap', function() {
$(this).data('touchstart', +new Date);
})
.on(eventType.mouseup + '.longtap', function(e) {
var now = +new Date,
than = $(this).data('touchstart');
now - than >= options.delay && options.onRelease && options.onRelease.call(this, e);
});
});
};
})(jQuery);
$('.long-tap').longTap({
delay: 1000, // really long tap
onRelease: function(e) {
c($(this).position(), e);
e.stopImmediatePropagation();
e.preventDefault();
alert('show context menu or something else:');
}
});
$('.long-tap').click(function(){
alert("click");
})
.test {
width: 200px;
height: 100px;
background-color: #DDD;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test long-tap"></div>
You can use contextmenu for right click event:
$(document).on("contextmenu",function(e){
e.preventDefault();
// do the stuff
});
When you long press with the finger in your mobile device then the context menu will appear.
There is really no good way to do such thing, what you can do is test if longtap event is registered on event target for click event handler:
$('.long-tap').click(function(){
if ($(this).data('touchstart')) {
return;
}
...
});
In general, I think your general approach to implement context menu for touch screen devices should be reconsidered.
Best of luck!
Related
In Hammer.js 2.02, how do you detect when a press gesture has ended?
I have a 'press' recognizer on an element with a time of 1 which prints a message as soon as the press event starts. But how do I check when the user has lifted their finger? Right now I'm using the 'pressup' recognizer which nearly works, except that it only triggers if the user has held their finger down for >500ms. How can I check when shorter presses end?
var pressOptions = {
event: 'press',
pointer: 1,
threshold: 5,
time: 1
};
var trackpadRight = $('#trackpad-right').hammer();
trackpadRight.data("hammer").get('press').set(pressOptions);
trackpadRight.bind('press', function(ev) {
console.log("PRESSS DOWN");
});
trackpadRight.bind('pressup', function(ev) {
console.log("PRESS UP");
});
Just use Hammer without jQuery.
var pressOptions = {
// event: 'press', //no need to pass defaults
// pointer: 1,
// threshold: 5,
time: 1
};
var trackpadRightTouch = new Hammer(document.getElementById('trackpad-right'));
trackpadRightTouch.get('press').set(pressOptions);
trackpadRightTouch.on('press', function(ev) {
console.log("PRESSS DOWN");
});
trackpadRightTouch.on('pressup', function(ev) {
console.log("PRESS UP");
});
this should work.
From your comment, I thought you need to detect the touch release. If yes then you can use the default "touchend" event by directly bind to the element, like below:
$('#trackpad-right').bind('touchend', function(ev) {
console.log("PRESS UP");
});
I did a search in the site but didn't get useful info for my case. A simple demo can be found at jsfiddle. When I moved onto the div scroll bar and pressed the mouse, the event 'mousedown' was fired. And then I scrolled the bar, released the mouse. Unfortunately the event 'mouseup' was not fired in IE/Safari. Chrome and Firefox work fine. Is there a work-around solution for IE and Safari? Thanks!
Sample Code :
<div class="box">
<div class="box-inner">
</div>
</div>
<p id="text"></p>
jQuery :
$(document).ready(function() {
var $txt = $('#text');
$('.box').mouseup(function() {
$txt.text('mouseup');
});
$('.box').mousedown(function() {
$txt.text('mousedown');
});
});
Did more search in the site and got similar question. A solution proposed in the question is not prefect but worthy of trying.
$(document).ready(function() {
var $txt = $('#text'), $win = $(window), $doc = $(document);
$win.on('mousedown', function(event) {
$txt.text('mousedown');
if(event.pageX < ($win.width() - 10)) {
return;
}
$doc.on('mousemove', function(event) {
if(event.pageX < ($win.width() - 10)){
//mouse is off scrollbar
$(this).unbind(event);
$win.trigger('mouseup', ['manual fire']);
}
});
}).on('mouseup', function(event, str) {
$txt.text('mouseup: ' + str);
});
});
the mouseup fired for me in Safari... I can't test in IE as I'm on a Mac, but please try this:
http://jsfiddle.net/CmW9e/4/
$(document).ready(function() {
var $txt = $('#text');
$('.box').on("mousedown.box", function() {
$txt.text('mousedown');
$(document).one("mouseup.box", function() {
$txt.text('mouseup');
});
});
});
Basically binding the mouseup to the Document when you mousedownand then removing the bind after it's triggered :) this way the mouseup will always trigger no matter where the mouse is
I have a draggable <div> with a click event and without any event for drag,
but after I drag <div> the click event is apply to <div>.
How can prevent of click event after drag?
$(function(){
$('div').bind('click', function(){
$(this).toggleClass('orange');
});
$('div').draggable();
});
http://jsfiddle.net/prince4prodigy/aG72R/
FIRST attach the draggable event, THEN the click event:
$(function(){
$('div').draggable();
$('div').click(function(){
$(this).toggleClass('orange');
});
});
Try it here:
http://jsfiddle.net/aG72R/55/
With an ES6 class (No jQuery)
To achieve this in javascript without the help of jQuery you can add and remove an event handler.
First create functions that will be added and removed form event listeners
flagged () {
this.isScrolled = true;
}
and this to stop all events on an event
preventClick (event) {
event.preventDefault();
event.stopImmediatePropagation();
}
Then add the flag when the mousedown and mousemove events are triggered one after the other.
element.addEventListener('mousedown', () => {
element.addEventListener('mousemove', flagged);
});
Remember to remove this on a mouse up so we don't get a huge stack of events repeated on this element.
element.addEventListener('mouseup', () => {
element.removeEventListener('mousemove', flagged);
});
Finally inside the mouseup event on our element we can use the flag logic to add and remove the click.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
e.target.addEventListener('click', preventClick);
} else {
e.target.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
In the above example above I am targeting the real target that is clicked, so if this were a slider I would be targeting the image and not the main gallery element. to target the main element just change the add/remove event listeners like this.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
element.addEventListener('click', preventClick);
} else {
element.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
Conclusion
By setting anonymous functions to const we don't have to bind them. Also this way they kind of have a "handle" allowing s to remove the specific function from the event instead of the entire set of functions on the event.
I made a solution with data and setTimeout. Maybe better than helper classes.
<div id="dragbox"></div>
and
$(function(){
$('#dragbox').bind('click', function(){
if($(this).data('dragging')) return;
$(this).toggleClass('orange');
});
$('#dragbox').draggable({
start: function(event, ui){
$(this).data('dragging', true);
},
stop: function(event, ui){
setTimeout(function(){
$(event.target).data('dragging', false);
}, 1);
}
});
});
Check the fiddle.
This should work:
$(function(){
$('div').draggable({
start: function(event, ui) {
$(this).addClass('noclick');
}
});
$('div').click(function(event) {
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');
}
else {
$(this).toggleClass('orange');
}
});
});
DEMO
You can do it without jQuery UI draggable. Just using common 'click' and 'dragstart' events:
$('div').on('dragstart', function (e) {
e.preventDefault();
$(this).data('dragging', true);
}).on('click', function (e) {
if ($(this).data('dragging')) {
e.preventDefault();
$(this).data('dragging', false);
}
});
You can just check for jQuery UI's ui-draggable-dragging class on the draggable. If it's there, don't continue the click event, else, do. jQuery UI handles the setting and removal of this class, so you don't have to. :)
Code:
$(function(){
$('div').bind('click', function(){
if( $(this).hasClass('ui-draggable-dragging') ) { return false; }
$(this).toggleClass('orange');
});
$('div').draggable();
});
With React
This code is for React users, checked the draggedRef when mouse up.
I didn`t use click event. The click event checked by the mouse up event.
const draggedRef = useRef(false);
...
<button
type="button"
onMouseDown={() => (draggedRef.current = false)}
onMouseMove={() => (draggedRef.current = true)}
onMouseUp={() => {
if (draggedRef.current) return;
setLayerOpened(!layerOpened);
}}
>
BTN
</button>
I had the same problem (tho with p5.js) and I solved it by having a global lastDraggedAt variable, which was updated when the drag event ran. In the click event, I just checked if the last drag was less than 0.1 seconds ago.
function mouseDragged() {
// other code
lastDraggedAt = Date.now();
}
function mouseClicked() {
if (Date.now() - lastDraggedAt < 100)
return; // its just firing due to a drag so ignore
// other code
}
I'm trying to detect the scroll event in Android browser (my specific version is 2.1, but U want it to work also on older versions). This seems impossible!
I first tried this:
document.addEventListener('scroll', function(){ alert('test'); }, false);
But nothing is triggered (except when the page load).
I thought: well, let's be crazy and emulate it by :
1. Detecting touchend
2. Polling the window.pageYOffset so we know when the window stops scrolling
3. Manually trigger a user function I want on scroll.
Unfortunately, the touchend event doesn't look to be triggered either... in fact, when we don't scroll and only tap the screen (touchstart + touchend), it works. As soon as we scroll the page in between (touchstart + touchmove + touchend), it breaks everything.
Now my most basic example only contains this:
document.addEventListener('touchend', function(){ alert('test'); }, false);
But the alert doesn't show up when we scroll with the finger and release the touch...
Does anyone has a suggestion?
Thanks.
You may want to crawl the source for JQuery Mobile, it supports android browsers and has scroll event listeners.
Or at least they say it does in the docs. :p
Here's the source
$.event.special.scrollstart = {
enabled: true,
setup: function() {
var thisObject = this,
$this = $( thisObject ),
scrolling,
timer;
function trigger( event, state ) {
scrolling = state;
var originalType = event.type;
event.type = scrolling ? "scrollstart" : "scrollstop";
$.event.handle.call( thisObject, event );
event.type = originalType;
}
// iPhone triggers scroll after a small delay; use touchmove instead
$this.bind( scrollEvent, function( event ) {
if ( !$.event.special.scrollstart.enabled ) {
return;
}
if ( !scrolling ) {
trigger( event, true );
}
clearTimeout( timer );
timer = setTimeout(function() {
trigger( event, false );
}, 50 );
});
}
};
I have elements on the page which are draggable with jQuery. Do these elements have click event which navigates to another page (ordinary links for example).
What is the best way to prevent click from firing on dropping such element while allowing clicking it is not dragged and drop state?
I have this problem with sortable elements but think it is good to have a solution for general drag and drop.
I've solved the problem for myself. After that I found that same solution exists for Scriptaculous, but maybe someone has a better way to achieve that.
A solution that worked well for me and that doesn't require a timeout: (yes I'm a bit pedantic ;-)
I add a marker class to the element when dragging starts, e.g. 'noclick'. When the element is dropped, the click event is triggered -- more precisely if dragging ends, actually it doesn't have to be dropped onto a valid target. In the click handler, I remove the marker class if present, otherwise the click is handled normally.
$('your selector').draggable({
start: function(event, ui) {
$(this).addClass('noclick');
}
});
$('your selector').click(function(event) {
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');
}
else {
// actual click event code
}
});
Solution is to add click handler that will prevent click to propagate on start of drag. And then remove that handler after drop is performed. The last action should be delayed a bit for click prevention to work.
Solution for sortable:
...
.sortable({
...
start: function(event, ui) {
ui.item.bind("click.prevent",
function(event) { event.preventDefault(); });
},
stop: function(event, ui) {
setTimeout(function(){ui.item.unbind("click.prevent");}, 300);
}
...
})
Solution for draggable:
...
.draggable({
...
start: function(event, ui) {
ui.helper.bind("click.prevent",
function(event) { event.preventDefault(); });
},
stop: function(event, ui) {
setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
}
...
})
I had the same problem and tried multiple approaches and none worked for me.
Solution 1
$('.item').click(function(e)
{
if ( $(this).is('.ui-draggable-dragging') ) return false;
});
does nothing for me. The item is being clicked after the dragging is done.
Solution 2 (by Tom de Boer)
$('.item').draggable(
{
stop: function(event, ui)
{
$( event.originalEvent.target).one('click', function(e){ e.stopImmediatePropagation(); } );
}
});
This works just fine but fails in one case- when I was going fullscreen onclick:
var body = $('body')[0];
req = body.requestFullScreen || body.webkitRequestFullScreen || body.mozRequestFullScreen;
req.call(body);
Solution 3 (by Sasha Yanovets)
$('.item').draggable({
start: function(event, ui) {
ui.helper.bind("click.prevent",
function(event) { event.preventDefault(); });
},
stop: function(event, ui) {
setTimeout(function(){ui.helper.unbind("click.prevent");}, 300);
}
})
This does not work for me.
Solution 4- the only one that worked just fine
$('.item').draggable(
{
});
$('.item').click(function(e)
{
});
Yep, that's it- the correct order does the trick- first you need to bind draggable() then click() event. Even when I put fullscreen toggling code in click() event it still didn't go to fullscreen when dragging. Perfect for me!
I'd like to add to this that it seems preventing the click event only works if the click event is defined AFTER the draggable or sortable event. If the click is added first, it gets activated on drag.
I don't really like to use timers or preventing, so what I did is this:
var el, dragged
el = $( '#some_element' );
el.on( 'mousedown', onMouseDown );
el.on( 'mouseup', onMouseUp );
el.draggable( { start: onStartDrag } );
onMouseDown = function( ) {
dragged = false;
}
onMouseUp = function( ) {
if( !dragged ) {
console.log('no drag, normal click')
}
}
onStartDrag = function( ) {
dragged = true;
}
Rocksolid..
lex82's version but for .sortable()
start: function(event, ui){
ui.item.find('.ui-widget-header').addClass('noclick');
},
and you may only need:
start: function(event, ui){
ui.item.addClass('noclick');
},
and here's what I'm using for the toggle:
$("#datasign-widgets .ui-widget-header").click(function(){
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');
}
else {
$(this).next().slideToggle();
$(this).find('.ui-icon').toggleClass("ui-icon-minusthick").toggleClass("ui-icon-plusthick");
}
});
A possible alternative for Sasha's answer without preventing default:
var tmp_handler;
.sortable({
start : function(event,ui){
tmp_handler = ui.item.data("events").click[0].handler;
ui.item.off();
},
stop : function(event,ui){
setTimeout(function(){ui.item.on("click", tmp_handler)}, 300);
},
In jQuery UI, elements being dragged are given the class "ui-draggable-dragging".
We can therefore use this class to determine whether to click or not, just delay the event.
You don't need to use the "start" or "stop" callback functions, simply do:
$('#foo').on('mouseup', function () {
if (! $(this).hasClass('ui-draggable-dragging')) {
// your click function
}
});
This is triggered from "mouseup", rather than "mousedown" or "click" - so there's a slight delay, might not be perfect - but it's easier than other solutions suggested here.
In my case it worked like this:
$('#draggable').draggable({
start: function(event, ui) {
$(event.toElement).one('click', function(e) { e.stopPropagation(); });
}
});
After reading through this and a few threads this was the solution I went with.
var dragging = false;
$("#sortable").mouseover(function() {
$(this).parent().sortable({
start: function(event, ui) {
dragging = true;
},
stop: function(event, ui) {
// Update Code here
}
})
});
$("#sortable").click(function(mouseEvent){
if (!dragging) {
alert($(this).attr("id"));
} else {
dragging = false;
}
});
the most easy and robust solution? just create transparent element over your draggable.
.click-passthrough {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: transparent;
}
element.draggable({
start: function () {
},
drag: function(event, ui) {
// important! if create the 'cover' in start, then you will not see click events at all
if (!element.find('.click-passthrough').length) {
element.append("<div class='click-passthrough'></div>");
}
},
stop: function() {
// remove the cover
element.find('.click-passthrough').remove();
}
});
Have you tried disabling the link using event.preventDefault(); in the start event and re-enabling it in the drag stopped event or drop event using unbind?
Just a little wrinkle to add to the answers given above. I had to make a div that contains a SalesForce element draggable, but the SalesForce element has an onclick action defined in the html through some VisualForce gobbledigook.
Obviously this violates the "define click action after the drag action" rule, so as a workaround I redefined the SalesForce element's action to be triggered "onDblClick", and used this code for the container div:
$(this).draggable({
zIndex: 999,
revert: true,
revertDuration: 0,
start: function(event, ui) {
$(this).addClass('noclick');
}
});
$(this).click(function(){
if( $(this).hasClass('noclick'))
{
$(this).removeClass('noclick');
}
else
{
$(this).children(":first").trigger('dblclick');
}
});
The parent's click event essentially hides the need to double-click the child element, leaving the user experience intact.
I tried like this:
var dragging = true;
$(this).click(function(){
if(!dragging){
do str...
}
});
$(this).draggable({
start: function(event, ui) {
dragging = true;
},
stop: function(event, ui) {
setTimeout(function(){dragging = false;}, 300);
}
});
for me helped passing the helper in options object as:
.sortable({
helper : 'clone',
start:function(),
stop:function(),
.....
});
Seems cloning dom element that is dragged prevented the bubbling of the event. I couldnĀ“t avoid it with any eventPropagation, bubbling, etc. This was the only working solution for me.
The onmousedown and onmouseup events worked in one of my smaller projects.
var mousePos = [0,0];
function startClick()
{
mousePos = [event.clientX,event.clientY];
}
function endClick()
{
if ( event.clientX != mousePos[0] && event.clientY != mousePos[1] )
{
alert( "DRAG CLICK" );
}
else
{
alert( "CLICK" );
}
}
<img src=".." onmousedown="startClick();" onmouseup="endClick();" />
Yes, I know. Not the cleanest way, but you get the idea.