auto scroll table on dragging of row - javascript

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();

Related

Is there a way to know when an element pass over another element?

I have a sticky element which needs to recognize when it passes over another element in order to disappear from the screen.
This is the element that should disappear:
.sticky-footer
.container
.row.sticky-row
.col-xs-6
// text
.col-xs-6
// text
When the user is scrolling down and it passes over this element:
hr#line-before-related-article
Is there a way to do that with css or jQuery?
You can listen on an event that fires every time the page is scrolled and then check if it's location is past your hr#line-before-related-article.
Here's an example:
$(window).on('scroll', function() {
if ($(this).scrollTop() + $(this).height() >= $('#line-before-related-article').position().top) {
$('.sticky-footer').hide();
} else if ($('#line-before-related-article').position().top >= $(this).scrollTop()) {
$('.sticky-footer').show();
}
})
If you want something that's a little more performant, you can use something called a debounce function...
For those of you who don't know what a debounce function does, it limits the rate at which a function can fire. A quick example: you have a resize listener on the window which does some element dimension calculations and (possibly) repositions a few elements. That isn't a heavy task in itself but being repeatedly fired after numerous resizes will really slow your site down. Why not limit the rate at which the function can fire?
More on that here: https://davidwalsh.name/javascript-debounce-function

Prevent child event from firing

I have a slider that I am currently making. I am making slow progress, but I am making progress nonetheless!
Currently I have this:
http://codepen.io/r3plica/pen/mEKyGG?editors=1011#0
There are 2 things you can do with this control, the first thing is you can drag left or right. The second thing you can do is click a "point" and it will scroll to the center.
The problem I have is that if I start dragging from a point, when I let go it will invoke the moveToCenter method.
I have tried to prevent this by adding
// Stop from accessing any child events
e.preventDefault();
e.stopPropagation();
to the end of the dragEventHandler, but this did not work.
I also have 2 boolean values options.drag and options.start. I though I might be able to use them somehow (if the drag has started and is enabled then don't perform the moveToCenter but this didn't work either.
Do anyone have any idea how to get this to work?
Maybe this will help. You can register your events in bubbling or capturing mode, using addEventListener method. It defines orders of processing your events - child -> parent (bubbling), or vice versa (capturing).
http://www.quirksmode.org/js/events_advanced.html
So, if you use addEventListener(event, handler, true), it will use capturing event mode.
Codepen:
http://codepen.io/anon/pen/bZKdqV?editors=1011
divs.forEach(function (div) {
div.addEventListener('click', function(e) {
e.stopPropagation();
console.log('parent');
}, true);
});
Be aware of browser support (IE9+). All modern browsers - yes, of course.
http://caniuse.com/#search=addeventlistener
Update
So it turned out to be easier than first approach. (no need for capturing)
Check out codepen:
http://codepen.io/anon/pen/QExjzV?editors=1010
Changes from your sample:
At the beginning of moveToCenter: function(e, options, animate) function
if (options.started) {
return;
}
In if (['mouseup', 'mouseleave'].indexOf(e.type) > -1):
setTimeout(function() {
options.started = false;
} , 100);
instead of
options.started = false;
Hope this helps.

Passing mouseover through element

I'm working on a regex-analyzer that has syntax highlighting as a feature.
My site uses an overlay of two contenteditable divs.
Because of the difficulty in getting the cursor position and maintaining that even as tags are added and subtracted, I decided the best route was to have two contenteditable divs, one on top of the other. The first (#rInput) is pretty plain. If not for some nuisance problems that made me switch from a textarea, it could be a textarea. The second (#rSyntax) gets its value from rInput and provides syntax highlighting. I make sure that both are always scrolled to the same position so that the overlay is perfect (However, I also use a transparent (rgba(...)) font on rSyntax, so that if a momentary sync-delay should occur, the text is still legible.)
In the lower portion snapshot above, the code of the contenteditable rSyntax is this:
<span class="cglayer">(test<span class="cglayer">(this)</span>string)</span>
While rInput, positioned exactly on top, contains only this
(test(this)string)
The problem with this method is that I want to offer some alt-tooltips (or a javascript version) when a user mouses over. When the user mouses over rInput, I would like to pass the mouseover event to elements of rSyntax.
I'd like mousing over (test ... string) to show "Capturing group 1", while mousing over (this) would show "Capturing group 2", or if it were (?:test), it would say "Non-capturing group".
The terminology is a little tough because searching for things like "above" and "below" pulls up a lot of results that have nothing to do with z-index.
I did find the css property pointer-events which sounded ideal for the briefest moment but doesn't allow you to filter pointer events (set rInput to receive clicks, scrolls, but pass mouseover through to rSyntax and its child-elements.
I found document.elementFromPoint which may offer a solution but I'm not really sure how. I thought I would try document.getElementById('rInput').getElementFromPoint(mouseX,mouseY), but this function is not available to child elements.
Theoretically, I could hide the rInput on mousemove, using a setTimeout to put it back quickly, and then a tooltip should appear when mousing over child elements of rSyntax, but that doesn't seem like the best solution because I don't want rSyntax to be clickable. Clicks should always go to rInput.
It's possible to disable the mouseover wizardry while rInput has the focus, but then tooltips from rSyntax won't work, and though I haven't tried this method, I'm not sure if passing a click event from rSyntax to rInput would position the cursor properly.
Is there a method to make this work?
Update
The best solution was to move the syntax highlighter rSyntax to an iframe because then I can pass elementFromPoint() without flickering the div which really is a vast improvement.
$(document).on("mousemove", "#rInput", function (e) {
$element = $(document.getElementById('frSyntax').contentDocument.elementFromPoint(e.pageX,e.pageY));
if ($element.attr("id") != "frSyntax" && $element.attr("id") != "rSyntax" && $element.attr("title") && $element.attr("title").length) {
$mother.find(".dashed").removeClass("dashed")
$element.addClass("dashed")
$("#syntip").html($element.attr("title"))
$("#syntip").css({"top": e.pageY+10, "left": e.pageX, "display": "inline-block"})
} else {
$mother.find(".dashed").removeClass("dashed");
$("#syntip").hide()
}
})
And at the beginning of my $(document).ready(..., I added this
$('#frSyntax').load(function(){
$mother = $("#frSyntax").contents();
//frSyntax is the name of the iframe.
$syntax = $mother.find("#rSyntax")
});
First Solution
I'll leave this here in case it's more like what someone wants.
From further research, I feel like the only approach is mousemove wizardry making the element disappear, finding the element below, with document.elementFromPosition, and popping it back in.
I'm still looking for advice if anyone has a better option.
The trouble is that this "flickering" will cause mousemove and mouseover to keep triggering, which is needless. So I created a variable to log the coords and only apply a change when the mouse actually moved.
$(document).on("mousemove", "#rInput", function (e) {
if (holdmouse != (e.pageX + "," + e.pageY)) {
$("#rInput").hide();
element = $(document.elementFromPoint(e.pageX,e.pageY));
if (element.attr("id") != "rInput" && element.attr("id") != "rSyntax") {
$(".classes, .cglayer").css("border-bottom","none");
element.css("border-bottom","2px dashed black")
$("#syntip").html(element.attr("title"))
$("#syntip").css({"top": e.pageY+10, "left": e.pageX, "display": "inline-block"})
holdmouse = e.pageX + "," + e.pageY
} else {
$(".classes, .cglayer").css("border-bottom","none");
$("#syntip").hide()
}
$("#rInput").show()
}
})

jQueryUI cannot work immediately after I set it

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?

js/jQuery Drag'n'Drop, recalculate the drop targets

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!

Categories

Resources