HTML5 Drag and drop. Detecting where element was grabbed - javascript

I am working with HTML 5 Drag and Drop API, and implementing sortable list and auto scroll. For some of features, I would like to able to detect which part of element was grabbed
Here is the illustration
Any help would be appreciated, thanks

You can use the computed padding+width of the element and the offsetX property of the MouseEvent to calculate the selected region.
yourElement.addEventListener('mousedown', function onDragStart(event){
let width = parseInt(window.getComputedStyle(yourElement).getPropertyValue('width'));
let padding = parseInt(window.getComputedStyle(yourElement).getPropertyValue('padding-left'));
let position = event.offsetX;
let middle = (width / 2) + padding;
if (position <= middle) {
console.log('left');
} else {
console.log('right');
}
});
See this jsfiddle for an example: https://jsfiddle.net/c23Lu6gy/28/

Related

How to prevent Read More/Read less button to jumping to the bottom?

I've created a button for Read More/Read Less functionality but when I'm clicking on the show less it jumps to the bottom. Could you please tell me how to fix this?...it should go to the same position...I'm using oxygen builder (code for this [ https://codepen.io/nick7961/pen/qByYMXZ?editors=0010
])
One way of doing this is to grab the current scroll y value and divide it by the body height to get the scroll position as a percentage. You'll have to do this in the event listener, before changes are made. In my function, setScroll, you can get the new body height and multiply it by the percentage you grabbed earlier, to keep the scroll in the same relative position.
button.addEventListener('click', () => {
const defaultValue = {
element: arrowIcon,
currentIcon: 'fa-chevron-down',
newIcon: 'fa-chevron-up',
};
//show content
if (initial.showAllContent){
showButton(buttonShowLess);
showButton(buttonShowMore, false);
content.classList.remove('gradientContent', 'maxContentHeight');
}else{
let relativeScroll = window.scrollY / document.body.clientHeight;
showButton(buttonShowLess, false);
showButton(buttonShowMore);
defaultValue.currentIcon = 'fa-chevron-up';
defaultValue.newIcon = 'fa-chevron-down';
content.classList.add('gradientContent', 'maxContentHeight');
setScroll(relativeScroll);
}
changeIcon(defaultValue);
initial.showAllContent = !initial.showAllContent;
});
function setScroll(relativeScroll) {
let scrollValue = document.body.clientHeight * relativeScroll;
window.scroll(0, scrollValue);
}
If you wanted to bounce the user back to the top, you could simply use:
window.scroll(0, 0);

Angular 2 window.scrollTo not Working?

I am trying to have it so a my div element, set to "overflow:auto" will scroll when the user is dragging an element.
Dragging the element works, and so does retrieving the proper mouse data (such as the x/y position upon initial drag (onDragStart) and also the current x/y position when the mouse is moving.
I realize my logic for the scrolling probably is off, but I am more just trying to get the div element to scroll at all. Any insight as to what is wrong would be greatly appreciated...Material Design is also being used.
Note: I am using ng-metadata which ports Angular 1 to look like Angular 2, really any guidance in either Angular 1 or Angular 2 would be helpful.
#autobind
onDragStart({clientX, clientY}) {
this.initY = clientY;
if (this.isDragging) return;
document.addEventListener('mousemove', this.dragListener = (e) => {
e.preventDefault();
if (Math.max(Math.abs(e.clientX - clientX), Math.abs(e.clientY - clientY)) > 3) {
document.removeEventListener('mousemove', this.dragListener);
document.addEventListener('mousemove', this.onDrag);
this.dragListener = this.onDrag;
this.fileService.dragSource = this.file;
// onDrag needs to be called before Angular can set the proper classes
this._element.toggleClass('dragging', this.isDragging);
this._element.toggleClass('bulk-dragging', this.inBulkDragOp);
this.onDragScroll(e);
this.onDrag(e);
this.drag.emit();
this._scope.$applyAsync();
}
});
}
#autobind
onDrag(e) {
console.log("Dragging...");
console.log(e);
var currentY = e.clientY;
var range = 100; // Range of 100px before scrolling begins... May need tweaking if too responsive
if (currentY > this.initY + range) {
console.log("SCROLL DOWN");
window.scrollTo(0, 500);
} else if (currentY < this.initY - range) {
console.log("SCROLL UP");
}
e.preventDefault();
this._element.css('transform', `translate(${e.clientX - this._element[0].clientWidth / 2}px, ${e.clientY - this._element[0].clientHeight / 2}px)`);
}
I had my container tag set to a height of 100% which seemed to break the functionality of the scroll. Removing it fixed the problem.

Position and Drag An Element With A Transform Applied

I have a javascript code that works perfectly for dragging object...but when I scaled the body down to 0.5...
transform:scale(0.5);
the position of mouse and the object dragged is not the same. How can I fix this? or is this possible?... thank you.
Heres a fiddle : http://jsfiddle.net/Xcb8d/65/
This was pretty interesting and makes me rethink all the locations I simply used offsetHeight and offsetWidth without knowing that if a transformation was applied to the element, these readonly properties in JavaScript would return incorrect.
The "trick" to this is the clientHeight/offsetHeight will not report their transformed properties correctly. In order to get the correct sizing information from the element you need to call getBoundingClientRect(). You then can calculate the correct pixel information of the scaled element, allowing you then to perform the correct positioning.
Retrieving the bounding rectangle allows you to get the pixel information off the viewport, you then can compare this information to the clientHeight within the browser to determine the scaled offset height, and position.
I modified some of the event wire ups just for testing. Also I added another class to produce a quarter sized object just to prove it works regardless of scale.
CSS:
html,
body {
width:100%;
height:100%;
}
.half-size
{
transform:scale(0.5);
-moz-transform:scale(0.5);
-webkit-transform:scale(0.5);
}
.quarter-size
{
transform:scale(0.25);
-moz-transform:scale(0.25);
-webkit-transform:scale(0.25);
}
#draggable-element {
width:100px;
height:100px;
background-color:#666;
color:white;
padding:10px 12px;
cursor:move;
position:relative; /* important (all position that's not `static`) */
display:block;
}
JavaScript:
var selected = null, // Object of the element to be moved
x_pos = 0, y_pos = 0, // Stores x & y coordinates of the mouse pointer
x_elem = 0, y_elem = 0; // Stores top, left values (edge) of the element
var elem_height = 0;
var elem_width = 0;
// Will be called when user starts dragging an element
function _drag_init(elem) {
// Store the object of the element which needs to be moved
selected = elem;
var boundingRectangle = selected.getBoundingClientRect();
y_elem = (selected.offsetHeight - (boundingRectangle.bottom - boundingRectangle.top)) / 2;
x_elem = (selected.offsetWidth - (boundingRectangle.right - boundingRectangle.left)) / 2
half_elem_height = (boundingRectangle.bottom - boundingRectangle.top) / 2;
half_elem_width = (boundingRectangle.right - boundingRectangle.left) /2;
document.addEventListener('mousemove', _move_elem, false);
document.addEventListener('mouseup', _destroy, false);
};
// Will be called when user dragging an element
function _move_elem(e)
{
x_pos = e.clientX;
y_pos = e.clientY;
selected.style.left = ((x_pos - x_elem) - half_elem_width) + 'px';
selected.style.top = ((y_pos - y_elem) - half_elem_height) + 'px';
}
// Destroy the object when we are done and remove event binds
function _destroy() {
selected = null;
document.removeEventListener('mousemove', _move_elem);
document.removeEventListener('mouseup', _destroy);
}
// Bind the functions...
document.getElementById('draggable-element').onmousedown = function () {
_drag_init(this);
};
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="draggable-element" class='half-size'>Drag me!</div>
</body>
</html>
Click below for a live preview
http://jsbin.com/moyadici/1/edit
(I prefer jsBin over jsFiddle for its live updates)
I did modify some of the event wire ups just for my initial testing. Also I broke the transform into a style so I could try other transforms. Tests look correct when rendering the 'quarter-size'. This just proves out it works regardless of scale and you don't need a magic number for your position calculations.
Not a real answer , but too long for a comment and because it is still jumpy, maybe not on first try ...
In Firefox, using pointer-events, you could try to restrain the area that will catch the mouse.
create an inside element right in center wich has the size of the element itself once scaled.
Your fiddle says scale(0.5) a square of 100px, so lets draw a square of 50px right in the middle with a pseudo element.
Set pointer-events to none to the element and reset it back to normal for the pseudo element. Now we have a square of 50px that will accept the mouse. once the element scaled down to 50px , we still have this area reacting. (the square only looks smaller, but keeping using same space.
To finalize your fiddle test, let's add to body : transform-origin:top left;, now we should be abble to drag this square.
Firefox test :http://jsfiddle.net/Xcb8d/78/
Chrome test : http://jsfiddle.net/Xcb8d/79/ (negative margins added )
after a few clicks, it really gets jumpy of limits :)
hope it gives some hints.
reading this : http://css-tricks.com/get-value-of-css-rotation-through-javascript/
I thought , we could get the transform value from body and update calculation within the function , so we can modify scale value without touching script
. rookie test : http://jsfiddle.net/Xcb8d/82/
ex: function updated from original fiddle
// Will be called when user dragging an element
function _move_elem(e) {
var el = window.document.body;
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("transform") ;
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
x_pos = document.all ? window.event.clientX : e.pageX;
y_pos = document.all ? window.event.clientY : e.pageY;
if (selected !== null) {
selected.style.left = ((x_pos / a) - x_elem) + 'px';
selected.style.top = ((y_pos / d) - y_elem) + 'px';
}
}

javascript (but not jQuery please) fixed position on x-axis but not y?

I've looked everywhere and so far have not found a non-jQuery js to handle this. I would like to avoid using a library for just this one simple task.
I would like to fix three navigation divs ("#header", "#tabs" and "#footer") to viewport left (or alternatively, to the x position of a div "#helper" with "position: fixed; left: 0; top: 0;") -- but not fix y. They can not be vertically fixed.
I've created a working js that forces the divs to reposition based on scrolling, but it's not smooth in the real page (too many dynamic and graphic elements) - I'd like it to either animate smoothly, or mimic fixed-left and not appear to reposition at all.
Anyone who can give pointers or a quick script, or review and modify the script I have made? I've noticed people tend to ask why an obvious solution is not used instead of answering the question... I will be glad to answer, but would prefer help with the actual problem.
Here is a jsFiddle with the problem: http://jsfiddle.net/BMZvt/6/
Thank you for any help!
Smooth animation example:
var box = document.getElementById('box');
var moveTo = function(obj, target) {
// start position
// you should obtain it from obj.style
var cpos = {
x: 0,
y: 0
}
var iv = setInterval(function(){
cpos.x += (target.x - cpos.x) * 0.3; // 0.3 is speed
cpos.y += (target.y - cpos.y) * 0.3; // 0.3 is speed
obj.style.left = Math.floor(cpos.x) + 'px';
obj.style.top = Math.floor(cpos.y) + 'px';
var dist = Math.abs(cpos.y - target.y); // distance (x+y) from destination
dist += Math.abs(cpos.x - target.x); // < 1 = object reached the destination
if(dist < 1) { // here we are checking is box get to the destination
clearInterval(iv);
}
}, 30); // this is also the speed
}
box.onclick = function(){
moveTo(box, {x: 90, y: 75}); // fire this function to move box to specified point
}
Demonstration: http://jsfiddle.net/Qwqf6/5/
Your script is your job, but this is a quick start how to solve animation problem
You can also do some fancy stuff with speed for example use sin(x) to set the speed
Demonstration #2 http://jsfiddle.net/Qwqf6/6/ (very smooth)
Full script here https://gist.github.com/3419179
I don't think there's a straight way to do this...
But here's a way.
First, You need to be able to detect the direction of the scrolling when window.onscroll event happens. You would do this by comparing the current page offsets with the newly acquired page offsets whenever the scroll event happens. (http://stackoverflow.com/questions/1222915/can-one-use-window-onscroll-method-to-include-detection-of-scroll-direction)
Now suppose you know the direction of the scroll, you want to change the styling for the divs depending on the direction of the scroll.
Let FixAtX be the value of the x coordinate that you want to fix your divs at.
Let OriginalY be the y coordinate of the divs.
Also whenever scrolling happens, despite of the direction, you want to remember the pageoffset X and Y. Let's call them OldX and OldY
If scrolling vertically:
Set position value for divs' style to be absolute.
Set top value for divs' style to be OriginalY
Set left value for divs' style to be OldX + FixAtX
If scrolling horizontally:
Set position value for divs' style to be fixed.
set top value for divs' style to be OriginalY - OldY (<- this may be different depending on how the browser computes pageOffset value,)
Set Left value for divs' style to be FixAtX
I think this should work...
Since you are just using browser's rendering for positioning, it should be very smooth!
hope I understood the question correctly.
This is for people who view this post - I wound up going with the solution I initially put together in the jsFiddle that used a simple javascript to mimic fixed x.
The javascript in the first answer was hefty and wound up buggy, and the second answer sounded good but did not work in practice. So, I'm recommending the javascript from the jsFiddle (below) as the best answer to fixed x and fluid y without a javascript library. It's not perfect and has a minimal delay but is the best answer I've found.
function fixLeft() {
function getScrollX() {
var x = 0, y = 0;
if( typeof( window.pageYOffset ) == 'number' ) {
x = window.pageXOffset;
} else if( document.body && ( document.body.scrollLeft) ) {
x = document.body.scrollLeft;
} else if( document.documentElement && ( document.documentElement.scrollLeft) ) {
x = document.documentElement.scrollLeft;
}
return [x];
}
var x = getScrollX();
var x = x[0];
// have to get and add horizontal scroll position px
document.getElementById('header').style.left = x + "px";
document.getElementById('tabs').style.left = x + "px";
document.getElementById('footer').style.left = x + "px";
}
window.onscroll = fixLeft;

Making a Javascript game, Having a little problem with scrolling

I have a #wrapper div and a #grid div nested inside. currently I can scroll around with this function below.
getCursorPos : function(){
// set the empty cursor object
var cursor = {};
//get the offset from the left of the grid container
var grid
//offset loop
$(function getCursorPos(){
grid = $('#grid').offset();
setTimeout(getCursorPos, game.loopSpeed);
});
//continuosly get the position
var that = this;
$(document).mousemove(function(e){
//if game mode is menu exit
if(game.mode === 'menu'){
return;
}
// NOTE: this looks a litle over done but don't remove anything
// its like this because javascript uses floating points
// and so in order to line up to the nearest hunderedth I
// had to make the cursor and div position intergers by
// muliplying by ten. one the two are added I reduced them
// and rounded them.
that.x = Math.round(((e.pageX * 10) - (grid.left * 10)) / 10);
that.y = Math.round(((e.pageY * 10) - (grid.top * 10)) / 10);
});
},
the problem is that the mouse coordinates only update when the mouse moves. is there any way to get the coordinates with out moving the mouse?
You always have the latest up-to-date coordinates of the mouse from the last mouse move, clarify why those are not useful to you.

Categories