https://jsfiddle.net/f8L300ug/3/
I'm trying to make a simple drag-to-resize function. See the example above.
If the mouse is released outside the <body>, the mouseup event won't be triggered, in contrast to what this post implies: Responding to the onmousemove event outside of the browser window in IE
jsFiddle's drag handler does a good job in this. If the mouse is released outside the element, then unbind the mousemove event; If the mouse is not released outside the element and moves back in, then keep the mousemove event. How can I achieve this?
Here's my code. jsFiddle's resize functionality is not perfect after well. After playing it for a while, I triggered a bug and my CSS panel is gone, so I can't post my CSS code here...
JS:
var initialX;
var leftPanel = document.getElementById("left");
var rightPanel = document.getElementById("right");
var resizePanels = function(e){
var deltaX = e.clientX - initialX;
var bodyWidth = parseInt(window.getComputedStyle(leftPanel).width);
initialX = e.clientX;
var newWidth = bodyWidth + deltaX;
leftPanel.style.width = newWidth + "px";
rightPanel.style.width = (parseInt(window.getComputedStyle(document.body).width) - newWidth)*0.9 + "px";
}
var bodyUnbindResize = function(){
document.body.removeEventListener("mousemove", resizePanels)
document.body.style.cursor = "default";
}
document.getElementById("dragger").addEventListener("mousedown", function(){
document.body.addEventListener("mousemove", resizePanels)
document.body.addEventListener("mouseup", bodyUnbindResize)
document.body.style.cursor = "col-resize";
});
HTML:
<div id="left" class="grid">
</div>
<div id="dragger" class="grid"></div>
<div id="right" class="grid">
</div>
And please improve my code if you have a better way. This is the first time I use vanilla JS to do this, so this might not be the best way. Thanks!
It doesn't seem to work when you attach the listener to document.body, but it does work if you attach it to window. If the mouse isn't released, even when you go out of the bounds of the window, then the browser will still detect the mouse move event, which means it is also able to detect when the mouse is released. This also means that when you go out of the window's bounds, you'll get a negative value. You can easily fix that.
https://jsfiddle.net/f8L300ug/8/
var initialX;
var leftPanel = document.getElementById("left");
var rightPanel = document.getElementById("right");
var resizePanels = function (e) {
// If the mouse is still down when dragging outside,
// to the left of the window, e.clientX will be negative
// and undesired behavior happens.
// To fix this, simply give it a value of 0 when it is below 0 (out of the window)
var clientX = e.clientX < 0 ? 0 : e.clientX;
var deltaX = clientX - initialX;
var bodyWidth = parseInt(window.getComputedStyle(leftPanel).width, 10); // < It's a good idea to always
var newWidth = bodyWidth + deltaX; // specify a radix for parseInt
initialX = clientX;
leftPanel.style.width = newWidth + "px";
rightPanel.style.width = (parseInt(window.getComputedStyle(document.body).width, 10) - newWidth) * 0.9 + "px";
}; // < It's a good idea to always close your `var` statements with a `;`
var bodyUnbindResize = function () {
window.removeEventListener("mousemove", resizePanels);
document.body.style.cursor = "default";
};
document.getElementById("dragger").addEventListener("mousedown", function () {
// Attach the mouse listeners to the window
window.addEventListener("mousemove", resizePanels);
window.addEventListener("mouseup", bodyUnbindResize);
document.body.style.cursor = "col-resize";
});
.grid{
height: 200px;
user-select:none;
webkit-touch-callout: none;
-webkit-user-select: none;
}
#left{
width: 50px;
border-right: 1px solid #ccc;
float:left;
background: black;
}
#dragger{
width: 5px;
float:left;
background-color:#eee;
cursor: col-resize;
}
#right{
width: 100px;
border-left: 1px solid #ccc;
overflow:hidden;
background: blue;
}
<div id="left" class="grid">
</div>
<div id="dragger" class="grid"></div>
<div id="right" class="grid">
</div>
You can also set a maximum width for your elements, so that they don't overflow the body. window.innerWidth gives you the current maximum width of the window.
Related
Suppose I have a large view with scroll in both x and y direction and instead of scrolling with mouse scroll, I need to drag and move to navigate through the entire view like in google maps
<div id="wrapper">
<div id="dragable_content">
<img src="https://geology.com/world/world-map.gif" style="height:100rem;"/>
</div>
</div>
how do I achieve this? preferably using only pure js
Thanks to the solution provided by #grisuu in the comments
I was able to whip up this:
jsfiddle
var _startX = 0;
var _startY = 0;
var _offsetX = 0;
var _offsetY = 0;
var _dragElement;
document.onmousedown = OnMouseDown;
document.onmouseup = OnMouseUp;
function OnMouseDown(event){
document.onmousemove = OnMouseMove;
_startX = event.clientX;
_startY = event.clientY;
_offsetX = document.getElementById('div1').offsetLeft;
_offsetY = document.getElementById('div1').offsetTop;
_dragElement = document.getElementById('div1');
}
function OnMouseMove(event){
_dragElement.style.left = (_offsetX + event.clientX - _startX) + 'px';
_dragElement.style.top = (_offsetY + event.clientY - _startY) + 'px';
}
function OnMouseUp(event){
document.onmousemove = null;
_dragElement=null;
}
html{
background-color:green;
overflow: hidden;
}
.div1{position:absolute; height:500px; width: 500px; z-index:1;}
<div class="div1" id="div1">
<div style="height:50px;width:50px;background-color:red;"></div>
<div style="position:absolute;top:75px;left:100px;height:50px;width:50px;background-color:blue;"></div>
</div>
I implemanted the same feature for my personal project by editing arsher's answer and I want to share the code I ended up with here.
Here are some notes:
This code does not use css at all, it's using scrollTo instead.
I'm using draggable='false' on the image to prevent browser dragging feature on images.
document.documentElement.scrollTop / .scrollLeft could have some browser support issues, there are many ways to get document scroll position value, many of them didn't work for me and this one did
/* check browser support, tested on Chrome v108
**********************************************************************/
let _startX = 0,
_startY = 0,
_scrollTop = 0,
_scrollLeft = 0;
document.onmousedown = OnMouseDown;
document.onmouseup = OnMouseUp;
function OnMouseDown(event) {
document.onmousemove = OnMouseMove;
_startX = event.clientX;
_startY = event.clientY;
_scrollTop = document.documentElement.scrollTop;
_scrollLeft = document.documentElement.scrollLeft;
}
function OnMouseMove(event) {
window.scrollTo({
left: _scrollLeft + (_startX - event.clientX),
top: _scrollTop + (_startY - event.clientY)
});
}
function OnMouseUp() {
document.onmousemove = null;
}
<div id="wrapper">
<div id="dragable_content">
<img draggable='false' src="https://geology.com/world/world-map.gif" style="height:100rem;" />
</div>
</div>
I have a drag and drop script that is relatively functional. However, I want to be able to trigger mouseup anywhere on the screen. Is there a way to trigger mouseup outside of the window, or outside of the current element? I know this is possible and I've seen other questions like this. I wanted to find a way in vanilla Javascript to detect mouseups like this.
document.onmousemove = mouseCoords;
var x = 0;
var y = 0;
var cl1= false;
var divid;
var offs1;
var offs2;
var topPos;
var leftPos;
function mouseCoords(e) {
x = e.x
y = e.y
if(cl1 === true){
document.getElementById(divid).style.top = topPos + (y-offs1) + 'px';
document.getElementById(divid).style.left = leftPos + (x-offs2) + 'px';
}
}
var drag = function(i, cas) {
divid= i
switch(cas){
case 1:
var rect = document.getElementById(divid).getBoundingClientRect();
leftPos = rect.left;
topPos = rect.top;
offs1 = y;
offs2 = x;
cl1= true;
break;
case 0:
offs1 = 0;
offs2 = 0;
cl1= false;
break;
}
}
#block{
width: 100px; z-index: 20; height: 50px; background-color: blue; position: fixed; user-select: none; -webkit-user-select: none;
}
.drag{
width: 200px; height: 100px; background-color: red; position: fixed;
}
<div id="block">mouseup doesn't trigger over me!</div>
<div id="1" class="drag" onmousedown="drag(1, 1)" onmouseup="drag(1, 0)"></div>
Use
document.addEventListener("mouseup", drag(null, 0));
for mouseup,
and this code for mousedown.
document.addEventListener("mousedown", drag(null, 1));
Basically, document.addEventListener works for the whole window. "mouseup" tells the script that the event is a mouseup, and the final bit is the function to be executed (drag(1, 0))
This question already has answers here:
How to detect when mousemove has stopped
(3 answers)
Closed 6 years ago.
I have an event that is triggered when a user has their mouse over a div element and while they're moving the mouse over the element. However, I can't seem to find a way to trigger an event when the mouse stops moving. For example, the event I'm working on checks if the user has the mouse over a div, if it is, then another element is shown and follows the mouse around. When the user leaves the div, the shown element is hidden. The way it works now is, I have the shown element showing and following the mouse around on the moused-over div, but it's not exactly what I want. I'd like to have it not show when the mouse is moving, but show when the mouse has stopped moving.
HTML
<div class="tooltip"><p></p></div>
<div class="holder" id="1"></div>
<div class="holder" id="2"></div>
CSS
.holder{
display: block;
float: left;
margin-bottom: 0.25%;
width: 100%;
height: 200px;
cursor: pointer;
border-top: 1px solid rgb(100, 100, 0);
border-bottom: 1px solid rgb(100, 100, 0);
}
.tooltip{
position: absolute;
display: none;
width: 150px;
background-color: white;
z-index: 5;
}
JS
var holder = document.getElementsByClassName("holder");
var tooltip = document.getElementsByClassName("tooltip")[0];
for(var i = 0; i < holder.length; i++){
var moving;
holder[i].onmouseover = function(ev){
moving = false
tooltip.style.display = "block";
tooltip.style.top = ev.clientY;
tooltip.style.left = ev.clientX;
}
holder[i].onmouseout = function(ev){
moving = false
tooltip.style.display = "none";
tooltip.style.top = ev.clientY;
tooltip.style.left = ev.clientX;
}
holder[i].onmousemove = function(ev){
moving = true;
}
if(moving){
tooltip.style.display = "none";
tooltip.style.top = ev.clientY;
tooltip.style.left = ev.clientX;
}
}
One naive approach would be set a timer when onmousemove is called. When the timer expires, do the hiding. When onmousemove is called again, reset the timer's timeout.
It's a judgement call for the application to make as to when someone stops moving the mouse. Technically a continuous movement of a mouse translates into a mouse move event per pixel.
You need to be a bit smart in doing this because onmousemove gets called a lot. So maybe something like this:
var mouseStartedMoving = false;
var mouseMoved = false;
const MINIMUM_MOUSE_MOVE_TIME = 100;
setInterval(() => {
if(!mouseMoved && mouseStartedMoving) {
//Mouse stopped moving
//Do CSS change
mouseStartedMoving = false;
}
mouseMoved = false;
}, MINIMUM_MOUSE_MOVE_TIME);
holder[i].onmousemove = function(ev){
mouseStartedMoving = true;
mouseMoved = true;
}
You can do this using "setTimeout" function.
When you track mouse moves set "mouseMoved" to current millis.
And then you do something like this:
setTimeout(function(){
var now = new Date();
var nowMillis = d.getMilliseconds();
var timeDiff = nowMillis - mouseMoved;
if (timeDiff > 3000)
alert("mouse stopped moving for 3 seconds");
}, 3000);
As a little practice I decided to recreate the Windows 8 Explorer file list panel, and all went well, until I wanted to add the mouse selection. It's basically the feature that allows you to select multiple files by dragging your mouse along the window, drawing a square, which should select all "files" that fall under it.
The only problem I have is that I can't seem to find a way to add the selected class to the elements under the selection
Here's the related code: (full code available in a working fiddle)
<ul class="files">
<li>
<span class="icon folder"></span>
<span class="name">Folder</span>
</li>
</ul>
.selection {
position: absolute;
border: 1px solid #39F;
background-color: rgba(51,153,255,.4);
display: none;
}
$(function(){
var $body = $(document.body);
var selecting = false,
$selectionDiv = $(document.createElement('div')).addClass('selection').appendTo($body),
startX, startY;
$body.on({
mousedown: function(e){
if (e.target === document.body){
e.stopPropagation();
startX = e.clientX;
startY = e.clientY;
selecting = true;
$selectionDiv.show().css({top:startY+'px',left:startX+'px',width:'0px',height:'0px'});
}
},
mousemove: function(e){
if (selecting){
var currentX = e.clientX,
currentY = e.clientY;
var subX = currentX - startX,
subY = currentY - startY;
if (subX < 0){
subX *= -1;
$selectionDiv.css('left',startX+(currentX-startX));
}
else $selectionDiv.css('left',startX+'px');
if (subY < 0){
subY *= -1;
$selectionDiv.css('top',startY+(currentY-startY));
}
else $selectionDiv.css('top',startY+'px');
$selectionDiv.css({
width: subX,
height: subY,
});
}
}
}).on('mouseup blur mouseleave',function(){
if (selecting){
$selectionDiv.hide();
selecting = false;
}
});
});
If I understand you correctly, you need to determine which elements are contained inside the selection box. Here's the code which seems to do the job (it's supposed to go into your mousemove event handler):
var topLeftX = Math.min(startX, currentX),
topLeftY = Math.min(startY, currentY),
bottomRightX = Math.max(startX, currentX),
bottomRightY = Math.max(startY, currentY);
$('.files li').each(function() {
var offset = $(this).offset(),
width = $(this).outerWidth(),
height = $(this).outerHeight();
if (offset.left < bottomRightX
&& offset.left + width > topLeftX
&& offset.top < bottomRightY
&& offset.top + height > topLeftY) {
$(this).addClass('selected');
}
else {
$(this).removeClass('selected');
}
});
This code goes through all the elements of your file list and runs rectangle overlap test (the algorithm for which I got from this answer) for the selection box and the list element. Usage of outerWidth() and outerHeight() makes sure that the border is also taken into consideration.
I also noticed that when you release the mouse your handler which resets the selection gets called:
$(window).click(function(){
$('.files li').removeClass('selected');
})
As a possible solution, you can move this into your mousedown handler.
Here's JSFIddle which works for me in Chrome 35: http://jsfiddle.net/5Hzm4/2/
I have two divs serving as two panels one to the left and one to the right.
They take 70% and 30% of the area.
I have a separator between them.
When I drag the separator to the left or right, I want that to remain as the position of the separator. i.e., I should be able to dynamically resize the left and right divs by dragging.
Here is the code I have:
http://jsbin.com/witicozi/1/edit
HTML:
<!DOCTYPE html>
<html>
<head>
<body>
<div style='height: 100px'>
<div id='left'>...</div>
<div id='separator'></div>
<div id='right'>...</div>
</div>
</body>
</html>
CSS:
#left {
float: left;
width: 70%;
height: 100%;
overflow: auto;
}
#separator {
float: left;
width: 3px;
height: 100%;
background-color: gray;
cursor: col-resize;
}
#right {
height: 100%;
overflow: auto;
}
JavaScript:
document.querySelector('#separator').addEventListener('drag', function (event) {
var newX = event.clientX;
var totalWidth = document.querySelector('#left').offsetWidth;
document.querySelector('#left').style.width = ((newX / totalWidth) * 100) + '%';
});
The problems:
The resizing happens, but the separator jumps around randomly. It even falls down many times. I have no idea what's happening.
The mouse cursor changes to a hand when the dragging begins. I want it to remain a col-resize.
It is very hard to drag.
No JQuery please.
If you use console.log(event), it shows that event.clientX doesn't return exactly what you are looking for. The following JavaScript worked for me in chrome.
document.getElementById('separator').addEventListener('drag', function(event) {
var left = document.getElementById('left');
var newX = event.offsetX + left.offsetWidth;
left.style.width = newX + 'px';
});
The event.offsetX value that it is returning is the location (in px) of the upper left hand corner of the left div. This will give you the same result but using percentages so that when the resize the window the columns adjust:
document.getElementById('separator').addEventListener('drag', function(event) {
var left = document.getElementById('left');
var newX = event.offsetX + left.offsetWidth;
left.style.width = (newX / window.innerWidth * 100) + '%';
});
Taking a bit of a different approach: rather than using the drag and drop functionality, I used some coupled mouse down and mouse up listeners. This has better cross-browser compatibility (at least as far as my testing goes) and it has the added benefit of being able to easily control the cursor.
var resize = function(event) {
var newX = event.clientX;
document.getElementById('left').style.width = (newX / window.innerWidth * 100) + '%';
};
document.getElementById('separator').addEventListener('mousedown', function(event) {
document.addEventListener('mousemove', resize);
document.body.style.cursor = 'col-resize';
});
document.addEventListener('mouseup', function(event) {
document.removeEventListener('mousemove', resize);
document.body.style.cursor = '';
});