I want to integrate both jQueryUI draggable and sortable in my project and distinguish them using the time difference between the starting time of mousedown and first mousemove events. Part of my code is as follows:
if (self.mouse.endTime - self.mouse.startTime < 700){
var deltaX = self.mouse.startX - event.pageX,
deltaY = self.mouse.startY - event.pageY;
// Drag down
if(Math.abs(deltaX) < Math.abs(deltaY) && deltaY < 0){
$(self.el).draggable({disabled: false});
$(self.el).find('li').draggable({disabled: true});
$(self.el).sortable({disabled: true});
$(self.el).draggable({axis: 'y'});
$(self.el).draggable({revert: true});
....
else {
$(self.el).sortable({disabled: false});
$(self.el).draggable({disabled: true}); // global dragging
$(self.el).children().draggable({disabled: true}); // item dragging
$(self.el).sortable({containment: $(self.el).closest('.content')});
}
But things are not what I expected. Every time I start dragging the element, the html shown in firebug has the right draggable/sortable class setting but it's not effective for current dragging events. For example, when you drag for the first time, the element is neither draggable nor sortable although the html has already had the corresponding class setting. It will be draggable or sortable the second time you drag it. And if you set the element draggable/sortable for current dragging event, it will work the next time you drag it. In a word, the draggable/sortable event you expect for current event will only be effective for the next event. I wanna know if this is what the jQueryUI should be or there is sth wrong with my code. How should I fix it?
Related
What's happening: Scrolling works no matter which position i have the mouse while i scroll.
What i want to achieve: When the user scrolls with the mouse pointer positioned inside a particular container, I would like to disable the plugin from changing pages. When the user scrolls with the mouse pointer outside that same container, the normal functionality of the plugin should be restored; i.e. the pages should be scrollable again.
What have i tried: I listened for the scroll event on the document and found out whether the mouse is inside the container while executing the scroll and store the possibilities as a boolean.
$(document).bind("mousewheel", function(event) {
// preventScroll = true;
console.log(event);
if($(event.target).closest(".no-scroll").length) {
preventScroll = true;
}
else {
preventScroll = false;
}
});
Then onLeave i try to find out the value of preventScroll and try to stop event propagation (since in want to stop an actual event) by returning false
setTimeout(function() {
console.log(preventScroll);
if(preventScroll) {
console.log("no-scroll")
return false;
}
}, 10);
I an using setTimeout to capture the desired value of preventScroll although I guess the plugin executes a scroll within that 10 ms and that's why return false doesn't seem to have an effect. I can't seem to figure out how else to proceed to achieve the desired functionality.
Codepen: https://codepen.io/binarytrance/pen/YxBqPj
In this implementation, the container i want to disable scroll is in the second page/section. Please be aware of the values spit out in the console.
Use the fullpage.js option normalScrollElements. Check the fullpage.js docs for more info:
normalScrollElements: (default null) If you want to avoid the auto scroll when scrolling over some elements, this is the option you need to use. (useful for maps, scrolling divs etc.) It requires a string with the jQuery selectors for those elements. (For example: normalScrollElements: '#element1, .element2'). This option should not be applied to any section/slide element itself.
I have spent some time searching and unable to find a solid solution without using buttons. I have a table that is X height and has many rows. I can simply scroll no problem, and with the plugin I am using I can reorder the rows. I am looking for a way to drag a row from one area of the table to another and if needed scroll the table in the correct direction. I am looking to do something like a OS desktop folder.
Plugin being used for reorder
I imagine adding a "mousemove" event to all elements while you're dragging the row.
Every time you move the mouse, you check if it's within (100 / 200 / 300)px of the window edge, start an interval loop (setInterval) that moves the screen by x pixels every y seconds.
You need to make sure that once you drop the row that you stop the interval loop and remove the mousemove event handlers from all elements. When you move out of the range near the edge of the window should also stop the interval loop.
If you want an idea of what the code would look like, I'll create a fiddle, let me know.
This is what I ended up doing:
$("#table-3 tr").mousedown(function() {
$(this).mousemove(function(e) {
// moving upward
if (e.pageY < mY) {
console.log('From Bottom');
clearInterval(intervalLoop);
intervalLoop = setInterval(function() {
$('#table-3').scrollTop($('#table-3').scrollTop() - 1);
}, 25);
// moving downward
} else {
console.log('From Top');
clearInterval(intervalLoop);
intervalLoop = setInterval(function() {
$('#table-3').scrollTop($('#table-3').scrollTop() + 1);
}, 25);
}
// set new mY after doing test above
mY = e.pageY;
});
}).mouseup(function() {
$("#table-3 tr").unbind('mousemove');
clearInterval(intervalLoop);
intervalLoop = 0;
});
Basicaly if you want to build your own functionality, you'll have to add it on to the unsupporting plugin you are using. Analyze that library to see what design patterns they use, thus knowing where they define theyr functionality. once you figured that out, and you know what road the developers chose to build their plugin you can expand it.
lets say you click on A than drag to B and release the mouse
a pseudo code for a function like that could be something like:
1) on mouse down event, check if the target is A
2) if its A, save a refferance to A object in a var, and save its innerHTML in a variable.
3) on mouse up event, check if the target is B
4) if its B, copy its innerHTML to the A's innerHTML, than copy the saved innerHTML ofA into B
You can also use the drag and drop API html5 standart defined for html elements, to catch the drop event.
memory menegment wise you should attach a single event handler to handle of the table element events, this is beacuse if you have 100 tags for example you dont want 100 event listeners saved in memory. in jQuery you can use $(elem).delegate();
I have setup a slider, here is jsfiddler http://jsfiddle.net/zZv5B/. How can I enable it for touch devises, I want to be able to swipe through panel area to slide next and prev slide. any idea would be really appreciated.
var currentIndex = 0;// store current pane index displayed
var ePanes = $('#slider .panel');// store panes collection
function showPane(index){// generic showPane
// hide current pane
ePanes.eq(currentIndex).stop(true, true).fadeOut();
// set current index : check in panes collection length
currentIndex = index;
if(currentIndex < 0) currentIndex = ePanes.length-1;
else if(currentIndex >= ePanes.length) currentIndex = 0;
// display pane
ePanes.eq(currentIndex).stop(true, true).fadeIn();
// menu selection
$('.nav li').removeClass('current').eq(currentIndex).addClass('current');
}
// bind ul links
$('.nav li').click(function(ev){ showPane($(this).index());});
// bind previous & next links
$('.previous').click(function(){ showPane(currentIndex-1);});
$('.next').click(function(){ showPane(currentIndex+1);});
// apply start pane
showPane(0);
Use the events (more here) 'touchstart' and 'touchend' and get the start X position and the end x position. You can then compare the two and determine which direction the touch/swipe has happend.
var xStart, xEnd;
$('.wrap').on('mousedown touchstart', function (e) {
//get start x position
xStart = e.pageX;
}).on('mouseup touchend', function (e) {
//get the end x position
xEnd = e.originalEvent.pageX;
if (xStart != xEnd) {
//swiped
if (xStart < xEnd) {
console.log('Right');
showPane(currentIndex + 1);
}
if (xStart > xEnd) {
console.log('Left');
showPane(currentIndex - 1);
}
}
});
example fiddle - not sure how browser compatibile this is.
Or you could just use my fave touch enabled slider swipejs
UPDATE:
To make it work correctly for mobile changed xEnd = e.originalEvent.pageX as per #User543294's comment
Also a new fiddle example to using e.changedTouches[0].pageX as per MDN documetation
I just came across TouchSwipe. It seems pretty nice, straightforward, and robust.
Maybe something like this?
var swipe_obj = {
swipeLeft: function(event, direction, distance, duration, fingerCount) {
$(".next").click(); },
swipeRight: function(event, direction, distance, duration, fingerCount) {
$(".previous").click(); },
};
$(".nav li").swipe(swipe_obj);
Many touch devices also trigger a click, after a touch event. They usually follow a secuence similar to this: a touch happens, then it will be fired the events "touchstart" -> "touchend" -> "click". Where that last click it's a fake one, and has a huge delay respect native events. But it also works.
So, if efficiencie don't care, don't worry. Your slider will work as you expected it will do.
But if you are interested in to achieve a good performance, I recommend you to handle native events directly. Which can be done using Modernizr dinamic testing.
It's a simple idea:
1 Download and link the Modernizr library into your code.
2 Test at the first line of your code which kind of events you can expect, based on device capabilities. A single line of javascript will be enough:
var actualEvent = (Modernizr.touch) ? 'touchstart' : 'click';
Extra points if you do this before onReady occurs.
3 Move your current handlers into the "on" form:
$('selector').on(actualEvent, callback)
Then you will be always handling the real event, not the simulated click fired by tablets and phones. Because the test at the beginning ensures you that "actualEvent" will be "touchstart" just on touchable devices and "click" for the rest.
Of course, you can substitute "touchstar" by the touch event you want. But I think that "touchstar" is the one who best suits your needs.
I'm looking to get an event to fire when one scrolls "up" from $(window).scrollTop == 0.
If you have the following code:
$(window).scroll(function(){
console.log("scrolling")
});
On a page where the document < window height then that event never fires because $(window).scrollTop isn't changing, but this doesn't mean that there's no mouse scroll input. I want an event to fire on mouse scroll regardless if the page is moving or not.
Seems like what you are looking for:
http://jsfiddle.net/n8eVQ/
$(document).on('mousewheel DOMMouseScroll MozMousePixelScroll', function(event, delta) {
console.log('mousewheel');
//you could trigger window scroll handler
$(window).triggerHandler('scroll');
});
Other way is to capture scroll event on modern browsers which support event capturing phase (IE>8). This can be used for any dynamic element. As jQuery doesn't implement capturing phase, you have to use javascript addEventListener() method. Here an example implementing logic to get scrolling direction for a textarea:
document.addEventListener('scroll', function (event) {
var $elm = $(event.target);
if ($elm.is('textarea')) { // or any other filtering condition
// do some stuff
var direction = $elm.scrollTop() > ($elm.data('scrollTop') || 0) ? "down" : "up";
$elm.data('scrollTop', $elm.scrollTop());
console.log('scrolling', direction);
}
}, true);
-DEMO-
document.addEventListener('DOMMouseScroll', callbackFunction, false);
Solution for firefox; for other browsers see #roasted solution
I have the following issue, I have a large tree which has subnodes which can be folded and unfolded on demand (the data within nodes gets fetched with AJAX). However, I use jquery.event.drop/drag to create my drag/drop targets.
However, when I fold/unfold the drop targets change position and I need to recalculate. This is how I wanted to do that:
function create_drop_targets() {
$('li a')
.bind('dropstart', function(event) {
})
.bind('drop', function(event) {
})
.bind('dropend', function(event) {
});
}
create_drop_targets() is called upon fold/unfold.
However, this doesn't work. I have located the following within jquery.event.drop:
var drop = $.event.special.drop = {
setup: function(){
drop.$elements = drop.$elements.add( this );
drop.data[ drop.data.length ] = drop.locate( this );
},
locate: function( elem ){ // return { L:left, R:right, T:top, B:bottom, H:height, W:width }
var $el = $(elem), pos = $el.offset(), h = $el.outerHeight(), w = $el.outerWidth();
return { elem: elem, L: pos.left, R: pos.left+w, T: pos.top, B: pos.top+h, W: w, H: h };
}
Now I need to know how I can call the setup() method again so it repopulates $elements with the new positions for the droppables.
Just had the same issue. I wandered around within the source-code of jQuery and found this (in ui.droppable.js):
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
...
So, you'd just have to use
$(".cocktails").draggable({
refreshPositions: true,
});
Seems not to be documented very much... but it fixed my problem. Makes everything a bit slower of course, I would advise some usage-dependent tweaking (enable it before the changes occur, and disable it once the user has moved his mouse and the changes have occured).
Maybe it will be better to add live events introduced in jQuery 1.3?
$("li a").live("dropstart", function(){...});
I ran into the same issue when I tried to combine scrolling with draggable rows in liteGrid, but I found a work-around. Your mileage may vary, but what I did was add logic to my drag event handler that would check to see if the grid was being scrolled (which is when I needed to force the droppable positions to be refreshed), and if so, I set refreshPositions to true on the draggable. This doesn't immediately refresh the positions, but it will cause them to refresh the next time the drag handle moves. Since refreshPositions slows things down, I then re-disable it the next time my drag event handler fires. The net result is that refreshPositions is enabled only when the grid is scrolling in liteGrid, and its disabled the rest of the time. Here's some code to illustrate:
//This will be called every time the user moves the draggable helper.
function onDrag(event, ui) {
//We need to re-aquire the drag handle; we don't
//hardcode it to a selector, so this event can be
//used by multiple draggables.
var dragHandle = $(event.target);
//If refreshOptions *was* true, jQueryUI has already refreshed the droppables,
//so we can now switch this option back off.
if (dragHandle.draggable('option', 'refreshPositions')) {
dragHandle.draggable('option', 'refreshPositions', false)
}
//Your other drag handling code
if (/* logic to determine if your droppables need to be refreshed */) {
dragHandle.draggable('option', 'refreshPositions', true);
}
}
$("#mydraggable").draggable({
//Your options here, note that refreshPositions is off.
drag: onDrag
});
I hope that saves you from ramming your head into the keyboard as many times as I did...
I realize the original question is quite old now, but one little trick I came up with to refresh the position of draggable elements without much overhead (AFAICT) is to disable and immediately re-enable them wherever appropriate.
For instance, I noticed that resizing my browser window would not refresh the position of my draggable table rows, so I did this:
$(window).resize(function () {
$(".draggable").draggable("option", "disabled", true);
$(".draggable").draggable("option", "disabled", false);
});
I hope this helps someone out there!