I have made a fiddle here and I am using Chrome.
I want to drag the red dashed/dotted line on the left to the right. A new flex column is added on mouseup OR when you exceed a part of the container, depending on the number of columns already added. For now I just try to add max 5 columns.
The first "drag" works as expected
mouse down
while holding mousedown drag to right
column is added on mouseup or when exceeding some width
Now I want to repeat these steps and add another one. But now the behaviour is different:
mouse down
drag it to the right but it gets stuck
I have to release the mouse button and move to the right and get out of the function
Here is some code, I've tried some stuff with bubble true or false on the eventlisteners but no luck. Should I use other events?
var container = document.querySelector('.js-flex-container'),
containerRow = container.querySelector('.js-flex-row'),
oldX = 0,
oldY = 0,
rect = container.getBoundingClientRect(),
mouseupEvent = new MouseEvent('mouseup'),
newDiv,
colCount,
captureMouseMove = function captureMouseMove(event){
var directionX = 0,
directionY = 0;
if ((event.clientX - rect.left) > oldX) {
// "right"
newDiv.style.width = oldX + 'px';
if (oldX >= Math.round(rect.right / colCount)) {
container.dispatchEvent(mouseupEvent);
}
}
oldX = (event.clientX - rect.left);
};
container.querySelector('.js-flex-column').addEventListener('mousedown', function(event){
var colWidth = event.clientX - rect.left,
columns = container.querySelectorAll('.col');
colCount = columns.length + 1;
newDiv = document.createElement('div');
newDiv.className = 'col-x';
columns[0].parentNode.insertBefore(newDiv, columns[0]);
container.addEventListener('mousemove', captureMouseMove);
});
container.addEventListener('mouseup', function(){
console.log('mouseup');
if (typeof newDiv !== 'undefined') {
newDiv.style.width = '';
newDiv.className = 'col col-' + colCount;
container.removeEventListener('mousemove', captureMouseMove);
}
});
Chrome is doing something (funky) with the event after move.
Adding event.preventDefault() should do the trick
captureMouseMove = function captureMouseMove(event){
var directionX = 0,
directionY = 0;
if ((event.clientX - rect.left) > oldX) {
// "right"
newDiv.style.width = oldX + 'px';
if (oldX >= Math.round(rect.right / colCount)) {
container.dispatchEvent(mouseupEvent);
}
}
oldX = (event.clientX - rect.left);
event.preventDefault(); // <---
};
I would also recommend that you don't use container for mouseup events. Instead use window so releasing outside of the container doesn't cause issues. You could do the same for mousemove.
Related
There is this jsFiddle. If you open it you will see a moveable div, but I also want to add the following thing: if you move this div to 'trash', that moveable div must disappear (you put it in trash). Thanks in advance!
My code for div moving:
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
// 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;
x_elem = x_pos - selected.offsetLeft;
y_elem = y_pos - selected.offsetTop;
}
// Will be called when user dragging an element
function _move_elem(e) {
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 - x_elem) + 'px';
selected.style.top = (y_pos - y_elem) + 'px';
}
}
// Destroy the object when we are done
function _destroy() {
selected = null;
}
// Bind the functions...
document.getElementById('draggable-element').onmousedown = function () {
_drag_init(this);
return false;
};
document.onmousemove = _move_elem;
document.onmouseup = _destroy;
Trash the div if it has more than 50% area in trash
Find x, y co-ordinates of trash and dragged elements
Find overlapped area between them
If the area is more than half of div area , hide it
Code
function getOffset(el) {
var _x = 0;
var _y = 0;
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return {
top: _y,
left: _x
};
}
function shouldTrash() {
var dragged = getOffset(document.getElementById('draggable-element'));
var x11 = dragged.left;
var x12 = dragged.left + 100;
var y11 = dragged.top;
var y12 = dragged.top + 100;
var trashed = getOffset(document.getElementById('trash'));
var x21 = trashed.left;
var x22 = x21 + 100;
var y21 = trashed.top;
var y22 = y21 + 100;
x_overlap = Math.max(0, Math.min(x12, x22) - Math.max(x11, x21));
y_overlap = Math.max(0, Math.min(y12, y22) - Math.max(y11, y21));
overlapArea = x_overlap * y_overlap;
if (overlapArea > 100 * 50) {
document.getElementById('draggable-element').style.display = 'none';
}
}
Implemented here
References :
Retrieve co-ordinates
Find overlapped area
For a working solution check my modification of your jsFiddle
In order to solve the problem, I have taken the overlap function suggested in this answer which uses Element.getBoundingClientRect()
function checkOverlap(element1, element2) {
var rect1 = element1.getBoundingClientRect();
var rect2 = element2.getBoundingClientRect();
return !(rect1.right < rect2.left ||
rect1.left > rect2.right ||
rect1.bottom < rect2.top ||
rect1.top > rect2.bottom)
}
// Destroy the object when we are done
function _destroy() {
// check if an element is currently selected
if (selected) {
// check if the selected item overlaps the trash element
if (checkOverlap(selected, document.getElementById('trash'))) {
// remove the selected item from the DOM
selected.parentElement.removeChild(selected);
}
}
selected = null;
}
Here is a quick (not full) implementation of what you asked.
https://jsfiddle.net/dimshik/posm3cwk/4/
I created an function isInTrash(element) that gets an element and returns true if it is in the trash.
The check i did was if the mouse pointer that holds the draggable element is located inside the trash area.
You should also add some kind of a feedback to the user when the element is overlapping the trash while dragging.
you can call the isInTrash function inside your function _move_elem(e) and change the color for example of the draggable element.
Here is the additional functionality of the feedback
https://jsfiddle.net/dimshik/posm3cwk/5/
I see other similar questions asked, but the answers don't actually solve the problem.
I have this event listener:
function bigButton(x, y, strTxt, doFunction)
{
var getID = document.getElementById("canvas_1");
if (getID.getContext)
{
var ctx = getID.getContext("2d");
var btnW = 150;
var btnH = 50;
var cx = x - btnW/2;
var cy = y - btnH/2;
var left = cx;
var right = cx + btnW;
var top = cy;
var bottom = cy + btnH;
bbWhite(cx, cy, btnW, btnH, strTxt);
getID.addEventListener("mousemove", function bbAnim(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var mouseX = mousePos.x;
var mouseY = mousePos.y;
if (mouseX >= left
&& mouseX <= right
&& mouseY >= top
&& mouseY <= bottom)
{
bbBlack(cx, cy, btnW, btnH, strTxt);
}
else
{
bbWhite(cx, cy, btnW, btnH, strTxt);
}
}, false);
getID.addEventListener("click", function bbClick(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var clickX = mousePos.x;
var clickY = mousePos.y;
if (clickX >= left
&& clickX <= right
&& clickY >= top
&& clickY <= bottom)
{
doFunction();
}
}, false);
}
}
I want to remove it, because once I click it, I want to clear the canvas and do other things. Yet I have to have a named function to remove it. As far as I know I can't have a named listener without losing all the variables used in the anonymous function. How do I have a named listener in this situation? This is one of the very first issues with events that I have come across with learning JavaScript for the canvas. I'm surprised this isn't one of the first things you find in any tutorial.
UPDATE:
I have made it into a named function, but I still have no way to remove it (and the mousemove event) after the button is clicked.
Removing it is pretty much the same as adding it
getID.addEventListener("click", handler, false);
function handler(event) {
this.removeEventListener('click', handler, false);
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var clickX = mousePos.x;
var clickY = mousePos.y;
if (clickX >= left
&& clickX <= right
&& clickY >= top
&& clickY <= bottom)
{
doFunction();
}
}
and the event and value of this stays the same wether or not you reference it by name or not.
I want to achieve 'Panning' in svg while 'dragging' an element in particular direction.
Let say i select an element and start 'dragging' it upward untill it reached top of screen, now my svg should pan upwards automatically, without causing any problem with dragging. how i can achieve this.?
i have made a small mockup of this, where user can select and drag elements. it also contain two button, which cause svg to pan upward and downward. I am achiveing 'Panning' by changing 'ViewBox' of svg. ( i have to use this logic, i cannot use any other solution);
here is the fiddle link : http://jsfiddle.net/9J25r/
Complete Code:-
addEventListener('mousedown', mousedown, false);
var mx, my;
var dx, dy;
var mainsvg = document.getElementById('svg');
var selectedElement;
var eleTx, eleTy;
function getSvgCordinates(event) {
var m = mainsvg.getScreenCTM();
var p = mainsvg.createSVGPoint();
var x, y;
x = event.pageX;
y = event.pageY;
p.x = x;
p.y = y;
p = p.matrixTransform(m.inverse());
x = p.x;
y = p.y;
x = parseFloat(x.toFixed(3));
y = parseFloat(y.toFixed(3));
return {x: x, y: y};
}
function mousedown(event) {
if (event.target.id === 'arrow_t') {
panning('up');
}
else if (event.target.id === 'arrow_b') {
panning('down');
}
else if (event.target.id.split('_')[0] === 'rect') {
selectedElement = event.target;
var translatexy = selectedElement.getAttribute('transform');
translatexy = translatexy.split('(');
translatexy = translatexy[1].split(',');
eleTx = translatexy[0];
translatexy = translatexy[1].split(')');
eleTy = translatexy[0];
eleTx = parseFloat(eleTx);
eleTy = parseFloat(eleTy);
var xy = getSvgCordinates(event);
mx = xy.x;
my = xy.y;
mx = parseFloat(mx);
my = parseFloat(my);
addEventListener('mousemove', drag, false);
addEventListener('mouseup', mouseup, false);
}
}
function drag(event) {
var xy = getSvgCordinates(event);
dx = xy.x - mx;
dy = xy.y - my;
selectedElement.setAttribute('transform', 'translate(' + (eleTx + dx) + ',' + (eleTy + dy) + ')');
}
function mouseup(event) {
removeEventListener('mousemove', drag, false);
removeEventListener('mouseup', mouseup, false);
}
function panning(direction) {
var viewBox = svg.getAttribute('viewBox');
viewBox = viewBox.split(' ');
var y = parseFloat(viewBox[1]);
if (direction === 'up')
{
y+=5;
}
else if (direction === 'down')
{
y-=5;
}
viewBox=viewBox[0]+' '+y+' '+viewBox[2]+' '+viewBox[3];
svg.setAttribute('viewBox',viewBox);
}
here is the fiddle link : http://jsfiddle.net/9J25r/
EDIT:- (UPDATE)
I use the solution of Ian , it works well on the sample, but when i applied it to my original application, it did not work. check the below gif. You can see the 'gap' between mouse pointer and element. how i can remove that? .
This is one way, I've just done it with the Y/vertical for the moment...
You may want to adjust it, so that if the cursor is off the screen it adjusts the viewBox automatically as well, depends how you want it to drag (otherwise you will need to keep wiggling it to kick the drag func in).
var viewBox = svg.getAttribute('viewBox');
viewBoxSplit = viewBox.split(' ');
if( ely < viewBoxSplit[1] ) {
panning('down');
} else if( ely + +event.target.getAttribute('height')> +viewBoxSplit[1] + 300 ) {
panning('up');
}
jsfiddle here
Hi Friends i am very very new to javascript and and J Query . now i want to create virtual mouse pad . i am created two div's one for mouse pad and second one is container in container i am taken another div for act as a cursor(class name is follower) .
in mouse pad div when ever mouse move follower div moving relative to the mouse position. now i want to generate click event using virtual cursor means click the buttons using follower.
Button1
Button2
MousePad
this is my js code
var mouseX = 0, mouseY = 0, limitX = 150-15, limitY = 150-15;
$('.container1').mousemove(function(e){
var offset = $('.container1').offset();
mouseX = Math.min(e.pageX - offset.left, limitX);
mouseY = Math.min(e.pageY - offset.top, limitY);
mouseX=mouseX*3;
mouseY=mouseY*3;
if (mouseX < 0) mouseX = 0;
if (mouseY < 0) mouseY = 0;
});
// cache the selector
var follower = $("#follower");
var xp = 0, yp = 0;
var loop = setInterval(function(){
// change 12 to alter damping higher is slower
xp += (mouseX - xp);
yp += (mouseY - yp) ;
follower.css({left:xp, top:yp});
}, 30);
$('.buttons span').bind('click',function(){
alert($(this).attr('title'));
});
JSbin Link
http://jsbin.com/AGAquhej/2/edit for code
http://jsbin.com/AGAquhej/1 Demo
i want generate click event using follower(moving in mouse pad)
can any one solve the problem how to generate fake click events
Thanks in advance
Using the some collision detection code from this SO answer https://stackoverflow.com/a/12180865/1481489 the following may work (untested, description is in the comments):
var overlaps = (function () { // this is the collision detection code
function getPositions( elem ) {
var pos, width, height;
pos = $( elem ).position();
width = $( elem ).width();
height = $( elem ).height();
return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];
}
function comparePositions( p1, p2 ) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function ( a, b ) {
var pos1 = getPositions( a ),
pos2 = getPositions( b );
return comparePositions( pos1[0], pos2[0] ) && comparePositions( pos1[1], pos2[1] );
};
})();
$('.container1').mousemove(function(e){
var offset = $('.container1').offset();
mouseX = Math.min(e.pageX - offset.left, limitX);
mouseY = Math.min(e.pageY - offset.top, limitY);
mouseX=mouseX*3;
mouseY=mouseY*3;
if (mouseX < 0) mouseX = 0;
if (mouseY < 0) mouseY = 0;
});
$('.container1').click(function(){
proxyTriggerEvent('click');
});
function proxyTriggerEvent(eventName) {
$('.container').find('a,input,.buttons span')
.each(function() { // and loop through them all for testing
if ( overlaps(follower,this) ) { // collision detection for each item
$(this).trigger(eventName); // click the specific element
return false; // break out of the loop
}
});
}
Update:
Fixed a bug where the selector was not targeting the buttons. I misread the tag as <span class="button1"> but it is really <span title="button1">. The selector is now .buttons span instead of .button1,.button2.
Removed the unnecessary filtering of follower with .not('#follower')
Moved the hit detection to the click handler of .container - this way it isn't being run on every single interval frame, only when it really counts.
Moved the event trigger proxy out of the click call, now any event can be triggered, and it can be called as a regular function, e.g.: proxyTriggerEvent('mouseup'); or proxyTriggerEvent('click');
How to make a element draggable without using jQuery UI?
I have this code:
<script type="text/javascript">
function show_coords(event)
{
var x=event.clientX;
var y=event.clientY;
var drag=document.getElementById('drag');
drag.style.left=x;
drag.style.top=y
}
</script>
<body style="height:100%;width:100%" onmousemove="show_coords(event)">
<p id="drag" style="position:absolute">drag me</p>
</body>
The problem is that I want to drag while the user the pressing the mouse button. I tried onmousedown but results were negative.
It will be quite easy as you get the concept.
function enableDragging(ele) {
var dragging = dragging || false, //Setup a bunch of variables
x, y, Ox, Oy,
enableDragging.z = enableDragging.z || 1,
current;
ele.onmousedown = function(ev) { //When mouse is down
current = ev.target;
dragging = true; //It is dragging time
x = ev.clientX; //Get mouse X and Y and store it
y = ev.clientY; // for later use.
Ox = current.offsetLeft; //Get element's position
Oy = current.offsetTop;
current.style.zIndex = ++enableDragging.z; //z-index thing
window.onmousemove = function(ev) {
if (dragging == true) { //when it is dragging
var Sx = ev.clientX - x + Ox, //Add the difference between
Sy = ev.clientY - y + Oy; // 2 mouse position to the
current.style.top = Sy + "px"; // element.
current.style.left = Sx + "px";
return false; //Don't care about this.
}
};
window.onmouseup = function(ev) {
dragging && (dragging = false); //Mouse up, dragging done!
}
};
}
enableDragging(document.getElementById("drag")); //draggable now!
var ele = document.getElementsByTagName("div");
for(var i = 0; i < ele.length; i++){ //Every div's is draggable
enableDragging(ele[i]); // (only when its "position"
} // is set to "absolute" or
// "relative")
http://jsfiddle.net/DerekL/NWU9G/
The reason why your code is not working is because the <div> will always follow where your cursor goes, and you are not actually dragging it. The top left corner will always follow your cursor, and this is not we wanted.
UPDATE
Now if you only want a grabber or something similar, just change this part of the script:
ele.onmousedown = function(ev) {
current = ev.target;
to
var grabber = document.createElement("div");
grabber.setAttribute("class", "grabber");
ele.appendChild(grabber);
grabber.onmousedown = function(ev) {
current = ev.target.parentNode;
Now you can only click on the grabber to start the dragging process.
http://jsfiddle.net/DerekL/NWU9G/7/