JQ UI Draggable on iOS: initiating dragging in taphold-handler - javascript

In our app we want to drop from one list to over. Problem is, when there are many items in list - it's impossible to scroll when elements are dragable.
As workaround we want to disable drag-ability of elements and enable it only when user makes a long tap on an element.
$('li').bind('taphold', function (event, ui) {
console.log('taphold');
clearAll(); // clearing all other catched
$(this).addClass('catched')
$(this).draggable('enable');
});
here is jsfiddle https://jsfiddle.net/nrxaqc34/10/
So far it works, but user needs to tap once more in order to drag. And would be nice if user could start dragging right after long tap.
This answer here https://stackoverflow.com/a/9922048/582727 doesn't work on iOS.
Maybe someone has an idea.

Does it make sense to use delay option? http://api.jqueryui.com/draggable/#option-delay
$("li").draggable().draggable( "option", "delay", 2000);
Fiddle: https://jsfiddle.net/dob3uegj/
EDIT:
jqueryui-touch-punch (http://touchpunch.furf.com/) added to fiddle for smartphone simulations:
https://jsfiddle.net/dob3uegj/1/

Related

How to check if an element is dragged outside JQuery UI Sortable?

I'm making a jQuery UI sortable and I need to detect when an element is dragged out of it. Sounds EXACTLY like a job for the out event - except for one tiny remark that I myself missed initially:
Note: This event is also triggered when a sortable item is dropped.
This is a major showstopper for me. Whenever the items are merely rearranged, the dragged item still gets an out event in the end. I've been racking my brain but so far to no awail. How do I detect if an out event is because the item was really dragged outside, or wheter it's just because the drag operation is stopping?
(If it matters, jQuery UI version 1.11.4)
Added:
OK, I think I myself fell victim to the "just the last nonsensical part left" trap. What I was trying to do was to have two sortables that can have items dragged between them. Like the example, but with the placeholder disappearing completely when the item is dragged outside all sortables. Which is why I wanted to keep track of which items are inside and which items are outside.
The way I solved it in the end was to drop this "requirement". It's better anyway if the placeholder stays, because it shows where the item will get dropped to when the mouse is released - even when it is outside.
If I did want to keep to this path however, I now see that the correct solution would be to hide the placeholder on an out event, and show it on an over event. There's an over even when the dragging starts, although no item is being dragged in from the outside.
Either way, although both approaches would solve my real problem, neither of them really answer the question that I asked, so I'll let it stay.
Not sure if I am hitting the mark here and there are some other pitfalls.
Example: https://jsfiddle.net/Twisty/83Lwxkrs/
JavaScript
$(function() {
$("#sortable1, #sortable2").sortable({
connectWith: ".connectedSortable",
placeholder: "sortable-placeholder",
out: function(e, ui) {
$(".sortable-placeholder").remove();
}
}).disableSelection();
});
out can be used to remove the placeholder. So when a dragged item leaves, poof, placeholder is gone. When it is dragged over another or the same, poof, placeholder is recreated.
Pitfall: Dropping the item outside of any sortable, the item drops to nothing. The placeholder is removed and so is the dragged item. I suspect this can be fixed, either by looking for drop or mouse up and recreating the placeholder before sortable tries to revert the position.
I think that is the right answer:
var interval;
var outside = false
$(button).addEventListener('mousedown',function(e) {
interval = setInterval(function() {
if (outside) {
//fire event
}
},1000); // 1000 ms between each frame
});
// This code will stop the interval if you move your mouse away from the field while still holding it.
$(button).addEventListener('mouseout',function(e) {
outside = true;
clearInterval(); //function to clear interval
});

Disable taphold default event, cross device

I'm struggling to disable default taphold browser event. Nothing that I have found on Google provided any help. I have only Android 4.4.4 mobile and Chrome dev tools for testing. I tried CSS fixes, such as webkit-touch-callout and others, but apparently they don't work for Android, also they don't work in Chrome dev tools.
I also tried detecting right click, (e.button==2), it doesn't work.
I came up with a solution, but it solves one problem and creates another. I just want to have a custom action for 'long press' event for selected anchors and I don't want the default pop up to appear (open in a new tab, copy link address, etc.)
This is what I did:
var timer;
var tap;
$("body").on("touchstart", my_selector, function(e) {
e.preventDefault();
timer = setTimeout(function() {
alert('taphold!');
tap=false;
},500);
});
$("body").on("touchend", my_selector, function() {
if(tap) alert('tap');
else tap=true;
clearTimeout(timer);
});
It successfully disables the default taphold event and context menu doesn't appear. However it also disables useful events, such as swipe. The links are in a vertical menu and the menu is higher than the screen, so a user has to scroll it. If he tries to scroll, starting on an anchor, it won't scroll, it will alert 'tap!'
Any ideas how could I disable taphold default or how could I fix this code so it disables only tap events and leave default swipe events enabled?
Edit: Now I thought about setting a timeout, if the pointer is in the same place for lets say 100ms, then prevent default action. However e.preventDefault(); doesn't work inside setTimeout callback.
So now I'm just asking about the simplest example. Can I prevent default actions after certain amount of time has passed (while the touch is still there).
And this is my whole problem in a fiddle. http://jsfiddle.net/56Szw/593/
This is not my code, I got this from http://www.gianlucaguarini.com/blog/detecting-the-tap-event-on-a-mobile-touch-device-using-javascript/
Notice that while swiping the box up and down, scrolling doesn't work.
I got the solution. It was so simple! I had no idea there's an oncontextmenu event. This solves everything:
$("body").on("contextmenu", my_selector, function() { return false; });
For an <img> I had to use event.preventDefault() instead of return false.
document.querySelector('img').addEventListener('contextmenu', (event) => {
event.preventDefault();
}

Dropdown plugin closing on scroll bar click

I'm in the process of teaching myself how to write a jQuery plugin. I am using the jquery-hover-dropdown-box as a base example. It's not just copy/paste though, I've made a number of changes trying to get a better understanding of it all. For example I'm not incorporating the hover event, I added a filter, and currently not using any defaults to name a few. Clicking on a div's scroll bar fires the blur event in I.E is the only post I've found with what looks like a good resolution to this and I tried implementing something similar but was unsuccessful.
Complete Example: jsFiddle
Issue:
I click in the input and the dropdown opens but the first time I click on the scroll bar, the dropdown closes. When I open the dropdown a second time and click on the scroll bar, it does not close (as I would expect). From what I can tell, my issue is in the blur on the input. I understand that when I click in the scroll bar, the input has lost focus. I tried to implement something similar to this post on Scrollbars not working on dropdown in IE8 but was unable to get it working.
Steps to Reproduce:
Click in the input to open the dropdown
Click anywhere in the scroll bar and the dropdown closes (should stay open and scroll)
Click in the input a second time and the dropdown opens
Click anywhere in the scroll bar and the dropdown stays open (as it should)
Question:
What am I doing wrong that is causing the dropdown to close only the first time I click on the scroll bar?
What I've Tried:
When I'm appending the ul to the div (currently commented out around line 68 in the jsFiddle), I added the code below. I figured that if I stopped the action from being triggered with a mousedown on the ul it would fix my issue. Although it did fix the issue in Chrome, it persists in IE8.
Update: I changed the code below from $list.mousedown... to $container.mousedown... since $list is the ul and $container is the div that contains it. My thought was that it extend the area. The result was the same though.
...
$container.append($list);
$list.mousedown(function(e) {
e.preventDefault();
});
...
Since this seemed to be close, I tried taking a similar approach in the blur event. The issue explained above happens when I use this code. In Chrome, clicking the scroll bar does not fire the blur event but in IE8, it does. The first time the dropdown is opened and you click in the scroll bar, it logs "hiding". Open the dropdown again and click the scroll bar and it logs "bind mousedown". Click anywhere outside the dropdown and it closes (as it should) and logs "hiding" (as it should). To me it seems backwards, but obviously I'm not understanding it correctly. (The code below is around line 134 in the jsFiddle)
Code edit: Updated with Goran.it suggestion to prevent multiple bindings from happening.
...
// where $dom is the 'div' containing the 'ul'
$dom.unbind('mousedown.auto_dropdown_box_ul')
.bind('mousedown.auto_dropdown_box_ul', function(e) {
console.log('bind mousedown');
e.preventDefault();
});
setTimeout(function() {
console.log('hiding');
$dom.addClass('auto_dropdown_hide').hide();
}, 100);
...
I've also tried removing the blur event. I know this would prevent the dropdown from closing if you tabbed out of the input but figured it was worth a try. In Chrome it works exactly how I expected, clicking outside the input closes the dropdown, clicking the scroll bar does not close it and tabbing out does not close it. In IE8, clicking outside the dropdown does not close it though, nor does it close when you tab out, but clicking in the scroll bar does work. This is the code I added after removing blur (it's not included in the jsFiddle).
// below where the 'blur' event was
$(document).click(function(e) {
if (e.target == dropdownArray[0].input[0] || e.target == dropdownArray[0].dom[0]) {
console.log('matches');
e.preventDefault();
} else {
console.log('does not match');
dropdownArray[0].dom.addClass('auto_dropdown_box_hide').hide();
}
});
Again, this is my first attempt, I'm still learning. I'm sure there are multiple things that I'm probably doing wrong, that I can improve, etc. Before I tackle those, I would just like to understand what I'm doing wrong here and what I need to do to correct it. After reading the plugin concepts, I know there is much for me to learn.
I found few issues on a first look, you should change the :
$dom.bind('mousedown.auto_dropdown_box_ul'
to:
$dom.unbind('mousedown.auto_dropdown_box_ul').bind('mousedown.auto_dropdown_box_ul'
To prevent multiple events binding to the dom node, you can also use .one event handling of jQuery.
In the same event handling you should also put:
console.log('bind mousedown');
e.preventDefault();
return false;
To be sure event is not firing.
Hope this helps (I'm not having IE8 for a long time now)
I believe I finally figured this one out. After multiple tries I thought I'd change up the format to one that seemed, at least to me, a little more straight forward.
Here is the complete jsFiddle
The underlying fix was correctly setting/adjusting which element has focus and when. Since mousedown executes before click, I stuck with that event on the dropdown. In the mousedown event, I set isVisible = true and set focus back on the input (although the latter is not completely necessary). In the blur event, I'm checking isVisible. If it's true, that means that a click happened in the scroll bar so don't close the dropdown. If it's false, close the dropdown. Throughout events, I'm keeping track of isVisible so I know it's state when blur executes. Again, I changed up the format so the two fiddles do look different. I'm sure I could go back and implement something similar to the original fiddle and get it working but I just liked this way more. Here is a snippet of the relevant changes:
{
// some code above
// where $list is the 'ul'
$list.bind('mousedown', methods.onDropdownMousedown);
// where $obj is the 'input'
$obj.bind('blur', methods.doOnBlur);
},
onDropdownMousedown: function(e) {
$input.focus(); // not really needed, just in case
isVisible = true;
},
doOnBlur: function(e) {
if (isVisible) {
$input.focus();
isVisible = false;
} else {
// where $container is the 'div' containing the list
$container.addClass('auto_dropdown_box_hide').hide();
isVisible = false;
}
isVisible = false;
}

jQuery-ui menu submenu not collapsing on mouseout

I'm playing around with the Jquery-ui menu widget and I'm trying to get some functionality that seems like it should be REALLY obvious... but I might be overlooking it, or maybe they did when they wrote it.
I was able to get the menu horizontal easily enough, and make the submenu come down vertically... but, for the life of me, I can't get the submenu to collapse when I mouseout of the menu. It just stays there until I click something else. REALLY annoying. As a designer, it goes against all of my sensibilities and I can't see a reason for it to do just hang there.
Did I break the thing, or do I need to add something special?
As a side note, I've been fiddling with the Jquery-ui js for a couple hours, editing the menu widget. I tinkered around here:
// DEFAULT FUNCTION:
// Clicks outside of a menu collapse any open menus
this._on( this.document, {
click: function( event ) {
if ( !$( event.target ).closest( ".ui-menu" ).length ) {
this.collapseAll( event );
}
// Reset the mouseHandled flag
this.mouseHandled = false;
},
// I ADDED THIS:
mouseout: function( event ) {
this.collapseAll( event );
}
});
It produced the behavior I was going for... sort of...
Now I have spotty mouse enter detection for my menu item with the submenu attached. Not really ok with putting a live site up where the menu only works some of the time. Any ideas? Am I doing this the hard way or the wrong way, or is there something I simply don't know about? I'm not the greatest with jQuery, just started in fact.
I'm using a setup that is basically an augmented version of the example on the jQuery UI demo page of the menu widget, so I don't think it's my html that's causing the problem.
I just had the same problem and find the solution here. I hope it works with you.
Ok, this just took some searching.
I used jplfl's example in the previous answer as a guide and went into the jquery-ui.js file, searching for any functions that had to do with the menu. I found line # 9715 where mouseleave was bound to "collapseAll" instead of calling a function which actually collapsed the menu. I changed it to this:
mouseleave: function( event ){
this.collapseAll( event, true );
},
And now it works.
So, Yeah. jplfl's answer was definitely helpful, but I thought this might be pertinent as it goes into a little more detail.
Aaron:
Just search for 'ui.menu' and scroll down till you see
mouseleave: "collapseAll"
line, for me it is 11507 (jquery-ui-1.10.3)
I'm not sure if this will work:
$(document).ready(function() {
$(“#menu”).menu({
mouseleave: function( event ){ this.collapseAll( event, true ); }
});
But I'm sure there is another option.
You may check this link:

Controlling dijit.MenuBar with MouseOver events

In Dojo, is it possible to configure dijit.MenuBar so that the menus are triggered by MouseOver and MouseOut events? Actually this behavior is available already, but it is switched on or off by initial or successive mouse click events - so initially, MouseOver would not cause menu popup, but if the user clicks on a menu item, the menubar then becomes responsive to MouseOver events. A successive mouse click would again switch off this behavior.
What I would like to have is menus and sub-menus popping up based on MouseOver events without interference from click events. Please check the examples at http://dojotoolkit.org/reference-guide/dijit/MenuBar.html to see what I mean.
Your question piqued my interest enough to make a working solution.
I checked the dijit._MenuBase source code at dijit/Menu.js and apparently there is a this.isActive flag that is checked before proceeding. So I created a subclass that just sets this flag as true beforehand:
_ActivateOnMouseoverMixin = dojo.declare(null, {
onItemHover: function(item){
if(!this.isActive){
this._markActive();
}
this.inherited(arguments);
}
});
ActiveMenuBar = dojo.declare([dijit.MenuBar, _ActivateOnMouseoverMixin], {});
As a bonus, you can also modify the delay with the popupDelay variable (I changed it to be faster in the example)
I have no idea if there is another, more sane, way to do the same thing.
Here is an example that extends the solution of 'Hugomg' to case of unhovering the menu and the sub-menu:
[enter link description here][1]
[1]: http://jsfiddle.net/vg10c9md/2/

Categories