How to implement a drag-and-drop div from scratch with JavaScript? - javascript
It should be a combination of CSS and JavaScript. The steps to do should be:
Make it on top of all other elements (which property to specify?)
Catch the event when it is clicked (which event to listen to?)
Move the div as mouse moves.
But what are the details?
The jQuery Way:
Check out the jQueryUI addons draggable and droppable.
Literally hundreds of hours have been invested into the jQuery framework to make complicated tasks like this almost trivial. Take advantage of the jQuery team's efforts to make programming rich cross-browser applications easier on us all ;)
Chuck Norris' Way:
If you insist on trying this with raw javascript. You'll want to do a few things. One, programmatically set all draggable items to a relative/absolute positioning. If you click a particular item, cause it's top/left values in CSS to reflect the changes made by the x,y axis of the mouse until the click is released. Additionally, you'll want to update the z-index of each draggable when it's clicked to bring it into view.
Tutorial: How to Drag and Drop with Javascript
make it absolute positioned, with a high z-index.
check for onmousedown of the div.
use the event's mouseX and mouseY attributes to move the div.
Here's an example from Javascript, the Definitive Guide (updated here):
/**
* Drag.js: drag absolutely positioned HTML elements.
*
* This module defines a single drag() function that is designed to be called
* from an onmousedown event handler. Subsequent mousemove event will
* move the specified element. A mouseup event will terminate the drag.
* If the element is dragged off the screen, the window does not scroll.
* This implementation works with both the DOM Level 2 event model and the
* IE event model.
*
* Arguments:
*
* elementToDrag: the element that received the mousedown event or
* some containing element. It must be absolutely positioned. Its
* style.left and style.top values will be changed based on the user's
* drag.
*
* event: ethe Event object for the mousedown event.
*
* Example of how this can be used:
* <script src="Drag.js"></script> <!-- Include the Drag.js script -->
* <!-- Define the element to be dragged -->
* <div style="postion:absolute; left:100px; top:100px; width:250px;
* background-color: white; border: solid black;">
* <!-- Define the "handler" to drag it with. Note the onmousedown attribute. -->
* <div style="background-color: gray; border-bottom: dotted black;
* padding: 3px; font-family: sans-serif; font-weight: bold;"
* onmousedown="drag(this.parentNode, event);">
* Drag Me <!-- The content of the "titlebar" -->
* </div>
* <!-- Content of the draggable element -->
* <p>This is a test. Testing, testing, testing.<p>This is a test.<p>Test.
* </div>
*
* Author: David Flanagan; Javascript: The Definitive Guide (O'Reilly)
* Page: 422
**/
function drag(elementToDrag, event)
{
// The mouse position (in window coordinates)
// at which the drag begins
var startX = event.clientX, startY = event.clientY;
// The original position (in document coordinates) of the
// element that is going to be dragged. Since elementToDrag is
// absolutely positioned, we assume that its offsetParent is the
//document bodt.
var origX = elementToDrag.offsetLeft , origY = elementToDrag.offsetTop;
// Even though the coordinates are computed in different
// coordinate systems, we can still compute the difference between them
// and use it in the moveHandler() function. This works because
// the scrollbar positoin never changes during the drag.
var deltaX = startX - origX, deltaY = startY - origY;
// Register the event handlers that will respond to the mousemove events
// and the mouseup event that follow this mousedown event.
if (document.addEventListener) //DOM Level 2 event model
{
// Register capturing event handlers
document.addEventListener("mousemove", moveHandler, true);
document.addEventListener("mouseup", upHandler, true);
}
else if (document.attachEvent) //IE 5+ Event Model
{
//In the IE event model, we capture events by calling
//setCapture() on the element to capture them.
elementToDrag.setCapture();
elementToDrag.attachEvent("onmousemove", moveHandler);
elementToDrag.attachEvent("onmouseup", upHandler);
// Treat loss of mouse capture as a mouseup event.
elementToDrag.attachEvent("onclosecapture", upHandler);
}
else //IE 4 Event Model
{
// In IE 4, we can't use attachEvent() or setCapture(), so we set
// event handlers directly on the document object and hope that the
// mouse event we need will bubble up.
var oldmovehandler = document.onmousemove; //used by upHandler()
var olduphandler = document.onmouseup;
document.onmousemove = moveHandler;
document.onmouseup = upHandler;
}
// We've handled this event. Don't let anybody else see it.
if (event.stopPropagation) event.stopPropagation(); // DOM Level 2
else event.cancelBubble = true; // IE
// Now prevent any default action.
if (event.preventDefault) event.preventDefault(); // DOM Level 2
else event.returnValue = false; // IE
/**
* This is the handler that captures mousemove events when an element
* is being dragged. It is responsible for moving the element.
**/
function moveHandler(e)
{
if (!e) e = window.event; // IE Event Model
// Move the element to the current mouse position, adjusted as
// necessary by the offset of the initial mouse-click.
elementToDrag.style.left = (e.clientX - deltaX) + "px";
elementToDrag.style.top = (e.clientY - deltaY) + "px";
// And don't let anyone else see this event.
if (e.stopPropagation) e.stopPropagation(); // DOM Level 2
else e.cancelBubble = true; // IE
}
/**
* This is the handler that captures the final mouseup event that
* occurs at the end of a drag.
**/
function upHandler(e)
{
if (!e) e = window.event; //IE Event Model
// Unregister the capturing event handlers.
if (document.removeEventListener) // DOM event model
{
document.removeEventListener("mouseup", upHandler, true);
document.removeEventListener("mousemove", moveHandler, true);
}
else if (document.detachEvent) // IE 5+ Event Model
{
elementToDrag.detachEvent("onlosecapture", upHandler);
elementToDrag.detachEvent("onmouseup", upHandler);
elementToDrag.detachEvent("onmousemove", moveHandler);
elementToDrag.releaseCapture();
}
else //IE 4 Event Model
{
//Restore the original handlers, if any
document.onmouseup = olduphandler;
document.onmousemove = oldmovehandler;
}
// And don't let the event propagate any further.
if (e.stopPropagation) e.stopPropagation(); //DOM Level 2
else e.cancelBubble = true; //IE
}
}
function closeMe(elementToClose)
{
elementToClose.innerHTML = '';
elementToClose.style.display = 'none';
}
function minimizeMe(elementToMin, maxElement)
{
elementToMin.style.display = 'none';
}
HTML5 Drag and Drop
If you are reading this in the year 2017 or later, you might want to have a look at the HTML5 Drag and Drop API:
https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API
Example:
<!DOCTYPE HTML>
<html>
<head>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
<style>
.draggable {
border: 1px solid black;
width: 30px;
height: 20px;
float: left;
margin-right: 5px;
}
#target {
border: 1px solid black;
width: 150px;
height: 100px;
padding: 5px;
}
</style>
</head>
<body>
<h1>Drag and Drop</h1>
<h2>Target</h2>
<div id="target" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<h2>Draggable Elements</h2>
<div id="draggable1" class="draggable" draggable="true" ondragstart="drag(event)"></div>
<div id="draggable2" class="draggable" draggable="true" ondragstart="drag(event)"></div>
<div id="draggable3" class="draggable" draggable="true" ondragstart="drag(event)"></div>
</body>
</html>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
.mydiv {
float: left;
width: 100px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<h2>Drag and Drop</h2>
<div id="div1" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)">
<img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" draggable="true" ondragstart="drag(event)" id="drag1" width="88" height="31">
</div>
<div id="div2" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div id="div3" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div id="div4" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
</body>
</html>
The standard Drag and Drop API is widely recognized to suck big hairy donkey balls. So I wouldn't recommend doing it from scratch. But since that's your question, there are one set of requirements for making something draggable, and one set of requirements for properly setting up a drop zone:
Dragging:
The dom node must have the "draggable" property set to true
Note: e.dataTransfer.setDragImage can be used to set an alternate drag image (the default is a transparent image of the dom node being dragged.
Note2: e.dataTransfer.setData can be used inside the dragstart event to set some data that can be gotten back from the drop event.
Dropping:
In the dragover event, e.preventDefault must be called
In the drop event, e.preventDefault must be called
Example:
<body>
<div id="dragme" draggable="true">Drag Me</div>
<div id="dropzone">Drop Here</div>
</body>
<script>
var dragme = document.getElementById('dragme')
var dropzone = document.getElementById('dropzone')
dragme.addEventListener('dragstart',function(e){
dropzone.innerHTML = "drop here"
})
dropzone.addEventListener('dragover',function(e){
e.preventDefault()
})
dropzone.addEventListener('drop',function(e){
e.preventDefault()
dropzone.innerHTML = "dropped"
})
</script>
However, there are a whole lot of gotchas in using this API, including that:
it takes a lot of work to distinguish between a dragmove event over a dropzone and a dragmove event related to a draggable item
dragmove fires even if your mouse isn't moving
dragleave and dragenter fire even if your mouse isn't moving in or out of the listening dom node (it fires whenever it crosses a child-parent bounary for some stupid reason)
And more..
A better way
I wrote a drag and drop library that makes it a ton easier to use the standard drag and drop API without all those gotchas. Check it out here:
https://github.com/fresheneesz/drip-drop
Yeah, you can use jQuery if you want a bloated library with far more functions than you need! Or if you want to be more of an elitist, use Waltern Zorn's drag and drop library, which is one tenth of the size.
To bring the div on top of other elements you have to assign it a high z-index. Additionally, you can set box-shadow to give a feedback to the user that the element is draggable.
You have to listen for a total of three events: mousedown, mouseup, and mousemove. On mousedown you have to attach a listener on mousemove, which tracks the mouse pointer movements and moves the div accordingly, and on mouseup you have to remove the listener on mousemove.
Moving the div with the mouse is a bit tricky. If you translate the div to the pointer's position, the pointer will always point to the top left corner of the div, even when you click at the bottom right corner. For this, you have to calculate the coordinate difference between the div (top left corner) and the mouse pointer, in the mousedown event handler. Then, you have to subtract that difference from the mouse position before translating the div to that position, in the mousemove event handler.
See the demo for a better idea.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body,
html {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
}
#box {
width: 100px;
height: 100px;
margin: auto;
background-color: lightblue;
}
#box:active {
border: 1px solid black;
box-shadow: 2px 2px 5px 5px #bbb6b6;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script>
var box = document.getElementById("box");
var diff = {};
var getBoxPos = function() {
return {
x: box.getBoundingClientRect().x,
y: box.getBoundingClientRect().y
};
};
var calcDiff = function(x, y) {
var boxPos = getBoxPos();
diff = {
x: x - boxPos.x,
y: y - boxPos.y
};
};
var handleMouseMove = function(event) {
var x = event.x;
var y = event.y;
x -= diff.x;
y -= diff.y;
console.log("X " + x + " Y " + y);
box.style.position = "absolute";
box.style.transform = "translate(" + x + "px ," + y + "px)";
};
box.addEventListener("mousedown", function(e) {
calcDiff(e.x, e.y);
box.addEventListener("mousemove", handleMouseMove, true);
});
box.addEventListener("mouseup", function(e) {
console.log("onmouseup");
box.removeEventListener("mousemove", handleMouseMove, true);
});
</script>
</html>
You can do this by using following code
$(function() {
$("#imageListId").sortable({
update: function(event, ui) {
getIdsOfImages();
} //end update
});
});
function getIdsOfImages() {
var values = [];
$('.listitemClass').each(function(index) {
values.push($(this).attr("id")
.replace("imageNo", ""));
});
$('#outputvalues').val(values);
}
/* text align for the body */
body {
text-align: center;
}
/* image dimension */
img {
height: 200px;
width: 350px;
}
/* imagelistId styling */
#imageListId {
margin: 0;
padding: 0;
list-style-type: none;
}
#imageListId div {
margin: 0 4px 4px 4px;
padding: 0.4em;
display: inline-block;
}
/* Output order styling */
#outputvalues {
margin: 0 2px 2px 2px;
padding: 0.4em;
padding-left: 1.5em;
width: 250px;
border: 2px solid dark-green;
background: gray;
}
.listitemClass {
border: 1px solid #006400;
width: 350px;
}
.height {
height: 10px;
}
<link href="https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>
Drag Drop feature
</title>
</head>
<body>
<h1 style="color:green">GeeksforGeeks</h1>
<b>Drag and drop using jQuery UI Sortable</b>
<div class="height"></div><br>
<div id = "imageListId">
<div id="imageNo1" class = "listitemClass">
<img src="images/geeksimage1.png" alt="">
</div>
<div id="imageNo2" class = "listitemClass">
<img src="images/geeksimage2.png" alt="">
</div>
<div id="imageNo3" class = "listitemClass">
<img src="images/geeksimage3.png" alt="">
</div>
<div id="imageNo4" class = "listitemClass">
<img src="images/geeksimage4.png" alt="">
</div>
<div id="imageNo5" class = "listitemClass">
<img src="images/geeksimage5.png" alt="">
</div>
<div id="imageNo6" class = "listitemClass">
<img src="images/geeksimage6.png" alt="">
</div>
</div>
<div id="outputDiv">
<b>Output of ID's of images : </b>
<input id="outputvalues" type="text" value="" />
</div>
</body>
</html>
Related
How to trigger on Hover over Border
I want to react if a user hovers over a border (or into its near). I got an table for ERD / UML diagrams and I want to give the user the opportunity to resize this table by dragging the tables border. I am working with jQuery and pure JS. My tables are rectangles and its positions are known (x1, x2, y1, y2, width , height , (x1 | y1 ) is top-left, (x2 | y2) is bottom-right ). Every table has the class "diagram" , so I thought about triggering the ."diagram".hover and check the mouse position, but this would be non performant. I am mainly searching for idears, but short examples would be great. Code update: http://codepad.org/3xr8H39m
Wrap it in a wrapper and prevent event happening in its children. See below: var border = document.getElementById("border"); border.onmouseover = function(e) { if(e.target !== e.currentTarget) return; console.log("border-hover") } #border { padding: 4px; background: blue; box-sizing: border-box; cursor: pointer; } .box{ height: 100px; width: 100%; background: white; cursor: default; } <div id="border"> <div class="box"></div> </div>
As I know, there is no special method to catch hovering over borders, so you will need some workaround. The first way is to create external container wrapped around the table, with some pixels padding, so you will have some border, and detect hovering over both external container and the inner table, like here: <script type="text/javascript"> $(function() { var innerHover = false; $('.inner_table') .on('mouseover', function() { innerHover = true; }) .on('mouseout', function() { innerHover = false; }); // check if we can start resizing $('.external_wrapper').on('click', function() { if (!innerHover) { // we can start resizing! } }); }); </script> <div class="external_wrapper" style="padding: 3px;"> <table class="inner_table"> ... </table> </div> The other way is to create thin additional divs along all four table borders and use them as click anchors. This way is user in jQueryUI dialog widget. It is much more flexible as you will not be glued to container borders.
.mouseleave() activate only when moving out of specific border
I'm trying to create a roll up div while mouse is over another div. It opens but I'd like it not to close when leaving through the bottom border. Is it possible using JS or JQuery? Here is my current code: $("#sell1").mouseenter(function(){ $("#rollup1").css("display","inline"); }); $("#rollup1").mouseleave(function(){ $("#rollup1").css("display","none"); }); $("#sell1").mouseleave(function(){ $("#rollup1").css("display","none"); });
When processing the mouseleave, you can get the dimensions of the element and see whether the event's pageY is below the element: $("#rollup1").mouseenter(function() { $("#status").text("Over the element..."); }); $("#rollup1").mouseleave(function(e) { var $this = $(this); var bottom = $this.offset().top + $this.height(); if (bottom < e.pageY) { $("#status").text("Left via bottom edge"); } else { $("#status").text("Left NOT via bottom edge"); } }); #rollup1 { width: 50px; height: 50px; border: 1px solid black; } <div id="status"> </div> <div id="rollup1"></div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
onMouseMove + onMouseDown to call function
I have a real problem over here. I want a function to get called only when an object is clicked and when when the mouse is moved over the object. Here is a (syntax-retarded) example to get your understanding: <div onMouseMove+onMouseDown="function()" ... ></div> I was thinking of a way to solve this. What if I make a onMouseDown that trigger a function that will change the name of my onMouseMove - function, and use a "filler" or a "substitute" function? Let me explain: <div id="object" onMouseMove="substituteFiller()" onMouseDown="nameChanger()" ... ></div> <script> function nameChanger(){ document.getElementById("object").onMouseMove = "theRealFunction()"; } </script> <script> function theRealFunction() ... When I move the mouse over the object nothing will happen, because the function substituteFiller() won't work. But, when the mouse has clicked on the object the onMouseMove- function will be the correct one, theRealFunction(), and theRealFunction() will now get called when the mouse moves. This way I could activate/call theRealFunction() only when the object is clicked and when the mouse moved. However, it does not work. To be clear: how can I change the name of the function that is being called? How can I make a function to be called only when an object is clicked and mouse moved? Best regards, hope you understood! If not, comment I guess!
Okay, all you need to do is separately register 3 event handlers: mouse down mouse up mouse move You will have a boolean flag toggled to true and false on mouse down and up respectively. Then, on the mouse move handler, you need to check if the flag is true, which would mean the mouse button is down. Finally, we check to see if the cursor has actually moved from its initial position. Here's the JSFiddle. var example = document.getElementById("example"); var position = { X: 0, Y: 0 }; example.onmousedown = function (down) { downFlag = true; // Record click position position.X = down.clientX; position.Y = down.clientY; }; example.onmouseup = function (up) { downFlag = false; }; example.onmousemove = function (move) { if (downFlag) { if (position.X !== move.clientX || position.Y !== move.clientY) { // Do stuff here } } };
Its a part from one of my web page..Check It out.May this will help you.. <div id="Log_frm"> <fieldset id="fld_1"> <!--<legend>Log In</legend>--> <div id="log_l" onmouseover="dv_in();" onmouseout="dv_out();" style="background-color:#0C93D4;font-size: 15px;height: 30px;padding: 7px 32px 0px 32px;font-weight:bold; float: left;-webkit-border-top-left-radius: 5px;"> Log In </div> <div id="log_r" onmouseover="dv_out();" onmouseout="dv_in();"style="background-color: #0C93D4;font-size: 15px;font-weight:bold; float: right;height: 30px;padding: 7px 14px 0px 12px;-webkit-border-top-right-radius: 5px;"> Need Any Help </div > </fieldset> </div> <style> #Log_frm { width: 250px; height: 60px; margin-top: 10px; position: absolute; font-size: 12px; float: right; right: 0px; } #Log_frm a { color: #fff; text-decoration: underline !important; left: auto; margin-right: auto; } <script type="text/javascript"> function dv_in() { log_l.style.backgroundColor="#06C"; log_r.style.backgroundColor="#0C93D4"; } function dv_out() { log_l.style.backgroundColor="#0C93D4"; log_r.style.backgroundColor="#06C"; } </script> Regds..
How to prevent 'Overscroll history navigation' without preventing touchstart?
I'm implementing a swipe-base navigation, and I'm running into trouble with Chrome. A newly implemented feature, 'Overscroll history navigation', is triggered when the page is dragged to the right, causing a jump back (to 'history -1'). To prevent this, I'd have to call .preventDefault() on touchstart, but this also disables everything from clicking links to scrolling. How do I prevent browser UI events without interfering with the standard page? Disabling the feature altogether by setting the appropriate flag in chrome fixes the issue, but isn't practical for a public-facing application. chrome://flags/#overscroll-history-navigation
I eventually figured out a solution: Chrome fires at least touchstart and touchmove before overscrolling. By tracking the direction of those events, an overscroll can be filtered from regular scrolling. I've written up the code for this Hammer.js issue.
I used html, body { width: 100%; height: 100%; margin: 0; overscroll-behavior: contain; } https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior
To leave scrolling vertical working and moving elemets horizontally you need to check if moving is more along x then y axis by comparing coordinates differences using Math.abs(), then decide to call .preventDefault() in touchmove event or not. Sounds little strange but i think it is the only way to control this behemoth feature "Overscroll history navigation". ble. $(function () { function extract(e) { if (e.changedTouches) { e = e.changedTouches; if (e['0']) e = e['0']; } return e; } var div = $('div').html('Drag me left and right and see that Overscroll is disabled & drag me up and down to see that scroll still works<br /><br /><br />'.repeat(300)); var di = div.get(0); // get native dom element var startx, lastx, lasty, l = false, active = false; di.addEventListener("touchstart", function (e) { active = true; e = extract(e); // init lastx = e.pageX; lasty = e.pageY; l = parseInt(div.css('left'), 10); startx = e.pageX; }, false); di.addEventListener("touchmove", function (ee) { if (active) { var e = extract(ee); // check if preventDefault to cancel Overscroll history navigation only if it's needed if (Math.abs(lastx - e.pageX) > Math.abs(lasty - e.pageY)) { ee.preventDefault(); } // update x y to next above calculation lastx = e.pageX; lasty = e.pageY; // repositioning div.css({left: (l + (e.pageX - startx))+'px'}) } }, false); di.addEventListener("touchend", function (e) { active = false; }, false); }); Complete example to test on touch devices: DEMO
listen to onscroll event on the container and store the 'scrollLeft' value this.offsetX = event.currentTarget.scrollLeft; listen to onwheel event on the same container and use this handler var offset = 0; document.getElementById("1") .addEventListener("scroll", function(event) { offset = event.currentTarget.scrollLeft; }); document.getElementById("1") .addEventListener("wheel", function(event) { // if we reach the left side of the scrollable content, and we scroll further -> block the event if (offset === 0 && event.deltaX <= 0) { event.preventDefault(); } }); .container { width: 100%; background-color: blue; overflow: auto; display: -webkit-box; display: -ms-flexbox; display: flex; } .element { display: inline-block; min-width: 100px; height: 50px; margin: 10px; background-color: red; } <div id="1" class="container"> <div class="element"> 1 </div> <div class="element"> 2 </div> <div class="element"> 3 </div> <div class="element"> 4 </div> <div class="element"> 5 </div> <div class="element"> 6 </div> <div class="element"> 7 </div> <div class="element"> 8 </div> </div>
It's 'touchmove' you will have to stopPropagation() AND preventDefault() inside the callback you can call whatever you need for checking the swipe and your navigation to happen.
How to scroll to an element inside a div?
I have a scrolled div and I want to have an event when I click on it, it will force this div to scroll to view an element inside. I wrote its JavasSript like this: document.getElementById(chr).scrollIntoView(true); but this scrolls all the page while scrolling the div itself. How to fix that? I want to say it like this: MyContainerDiv.getElementById(chr).scrollIntoView(true);
You need to get the top offset of the element you'd like to scroll into view, relative to its parent (the scrolling div container): var myElement = document.getElementById('element_within_div'); var topPos = myElement.offsetTop; The variable topPos is now set to the distance between the top of the scrolling div and the element you wish to have visible (in pixels). Now we tell the div to scroll to that position using scrollTop: document.getElementById('scrolling_div').scrollTop = topPos; If you're using the prototype JS framework, you'd do the same thing like this: var posArray = $('element_within_div').positionedOffset(); $('scrolling_div').scrollTop = posArray[1]; Again, this will scroll the div so that the element you wish to see is exactly at the top (or if that's not possible, scrolled as far down as it can so it's visible).
You would have to find the position of the element in the DIV you want to scroll to, and set the scrollTop property. divElem.scrollTop = 0; Update: Sample code to move up or down function move_up() { document.getElementById('divElem').scrollTop += 10; } function move_down() { document.getElementById('divElem').scrollTop -= 10; }
Method 1 - Smooth scrolling to an element inside an element var box = document.querySelector('.box'), targetElm = document.querySelector('.boxChild'); // <-- Scroll to here within ".box" document.querySelector('button').addEventListener('click', function(){ scrollToElm( box, targetElm , 600 ); }); ///////////// function scrollToElm(container, elm, duration){ var pos = getRelativePos(elm); scrollTo( container, pos.top , 2); // duration in seconds } function getRelativePos(elm){ var pPos = elm.parentNode.getBoundingClientRect(), // parent pos cPos = elm.getBoundingClientRect(), // target pos pos = {}; pos.top = cPos.top - pPos.top + elm.parentNode.scrollTop, pos.right = cPos.right - pPos.right, pos.bottom = cPos.bottom - pPos.bottom, pos.left = cPos.left - pPos.left; return pos; } function scrollTo(element, to, duration, onDone) { var start = element.scrollTop, change = to - start, startTime = performance.now(), val, now, elapsed, t; function animateScroll(){ now = performance.now(); elapsed = (now - startTime)/1000; t = (elapsed/duration); element.scrollTop = start + change * easeInOutQuad(t); if( t < 1 ) window.requestAnimationFrame(animateScroll); else onDone && onDone(); }; animateScroll(); } function easeInOutQuad(t){ return t<.5 ? 2*t*t : -1+(4-2*t)*t }; .box{ width:80%; border:2px dashed; height:180px; overflow:auto; } .boxChild{ margin:600px 0 300px; width: 40px; height:40px; background:green; } <button>Scroll to element</button> <div class='box'> <div class='boxChild'></div> </div> Method 2 - Using Element.scrollIntoView: Note that browser support isn't great for this one var targetElm = document.querySelector('.boxChild'), // reference to scroll target button = document.querySelector('button'); // button that triggers the scroll // bind "click" event to a button button.addEventListener('click', function(){ targetElm.scrollIntoView() }) .box { width: 80%; border: 2px dashed; height: 180px; overflow: auto; scroll-behavior: smooth; /* <-- for smooth scroll */ } .boxChild { margin: 600px 0 300px; width: 40px; height: 40px; background: green; } <button>Scroll to element</button> <div class='box'> <div class='boxChild'></div> </div> Method 3 - Using CSS scroll-behavior: .box { width: 80%; border: 2px dashed; height: 180px; overflow-y: scroll; scroll-behavior: smooth; /* <--- */ } #boxChild { margin: 600px 0 300px; width: 40px; height: 40px; background: green; } <a href='#boxChild'>Scroll to element</a> <div class='box'> <div id='boxChild'></div> </div>
Native JS, Cross Browser, Smooth Scroll (Update 2020) Setting ScrollTop does give the desired result but the scroll is very abrupt. Using jquery to have smooth scroll was not an option. So here's a native way to get the job done that supports all major browsers. Reference - caniuse // get the "Div" inside which you wish to scroll (i.e. the container element) const El = document.getElementById('xyz'); // Lets say you wish to scroll by 100px, El.scrollTo({top: 100, behavior: 'smooth'}); // If you wish to scroll until the end of the container El.scrollTo({top: El.scrollHeight, behavior: 'smooth'}); That's it! And here's a working snippet for the doubtful - document.getElementById('btn').addEventListener('click', e => { e.preventDefault(); // smooth scroll document.getElementById('container').scrollTo({top: 175, behavior: 'smooth'}); }); /* just some styling for you to ignore */ .scrollContainer { overflow-y: auto; max-height: 100px; position: relative; border: 1px solid red; width: 120px; } body { padding: 10px; } .box { margin: 5px; background-color: yellow; height: 25px; display: flex; align-items: center; justify-content: center; } #goose { background-color: lime; } <!-- Dummy html to be ignored --> <div id="container" class="scrollContainer"> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div id="goose" class="box">goose</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> </div> <button id="btn">goose</button> Update: As you can perceive in the comments, it seems that Element.scrollTo() is not supported in IE11. So if you don't care about IE11 (you really shouldn't, Microsoft is retiring IE11 in June 2022), feel free to use this in all your projects. Note that support exists for Edge! So you're not really leaving your Edge/Windows users behind ;) Reference
To scroll an element into view of a div, only if needed, you can use this scrollIfNeeded function: function scrollIfNeeded(element, container) { if (element.offsetTop < container.scrollTop) { container.scrollTop = element.offsetTop; } else { const offsetBottom = element.offsetTop + element.offsetHeight; const scrollBottom = container.scrollTop + container.offsetHeight; if (offsetBottom > scrollBottom) { container.scrollTop = offsetBottom - container.offsetHeight; } } } document.getElementById('btn').addEventListener('click', ev => { ev.preventDefault(); scrollIfNeeded(document.getElementById('goose'), document.getElementById('container')); }); .scrollContainer { overflow-y: auto; max-height: 100px; position: relative; border: 1px solid red; width: 120px; } body { padding: 10px; } .box { margin: 5px; background-color: yellow; height: 25px; display: flex; align-items: center; justify-content: center; } #goose { background-color: lime; } <div id="container" class="scrollContainer"> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div id="goose" class="box">goose</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> <div class="box">duck</div> </div> <button id="btn">scroll to goose</button>
Code should be: var divElem = document.getElementById('scrolling_div'); var chElem = document.getElementById('element_within_div'); var topPos = divElem.offsetTop; divElem.scrollTop = topPos - chElem.offsetTop; You want to scroll the difference between child top position and div's top position. Get access to child elements using: var divElem = document.getElementById('scrolling_div'); var numChildren = divElem.childNodes.length; and so on....
If you are using jQuery, you could scroll with an animation using the following: $(MyContainerDiv).animate({scrollTop: $(MyContainerDiv).scrollTop() + ($('element_within_div').offset().top - $(MyContainerDiv).offset().top)}); The animation is optional: you could also take the scrollTop value calculated above and put it directly in the container's scrollTop property.
We can resolve this problem without using JQuery and other libs. I wrote following code for this purpose: You have similar structure -> <div class="parent"> <div class="child-one"> </div> <div class="child-two"> </div> </div> JS: scrollToElement() { var parentElement = document.querySelector('.parent'); var childElement = document.querySelector('.child-two'); parentElement.scrollTop = childElement.offsetTop - parentElement.offsetTop; } We can easily rewrite this method for passing parent and child as an arguments
Another example of using jQuery and animate. var container = $('#container'); var element = $('#element'); container.animate({ scrollTop: container.scrollTop = container.scrollTop() + element.offset().top - container.offset().top }, { duration: 1000, specialEasing: { width: 'linear', height: 'easeOutBounce' }, complete: function (e) { console.log("animation completed"); } });
None of other answer fixed my issue. I played around with scrollIntoView arguments and managed to found a solution. Setting inline to start and block to nearest prevents parent element (or entire page) to scroll: document.getElementById(chr).scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
There are two facts : 1) Component scrollIntoView is not supported by safari. 2) JS framework jQuery can do the job like this: parent = 'some parent div has css position==="fixed"' || 'html, body'; $(parent).animate({scrollTop: $(child).offset().top}, duration)
Here's a simple pure JavaScript solution that works for a target Number (value for scrollTop), target DOM element, or some special String cases: /** * target - target to scroll to (DOM element, scrollTop Number, 'top', or 'bottom' * containerEl - DOM element for the container with scrollbars */ var scrollToTarget = function(target, containerEl) { // Moved up here for readability: var isElement = target && target.nodeType === 1, isNumber = Object.prototype.toString.call(target) === '[object Number]'; if (isElement) { containerEl.scrollTop = target.offsetTop; } else if (isNumber) { containerEl.scrollTop = target; } else if (target === 'bottom') { containerEl.scrollTop = containerEl.scrollHeight - containerEl.offsetHeight; } else if (target === 'top') { containerEl.scrollTop = 0; } }; And here are some examples of usage: // Scroll to the top var scrollableDiv = document.getElementById('scrollable_div'); scrollToTarget('top', scrollableDiv); or // Scroll to 200px from the top var scrollableDiv = document.getElementById('scrollable_div'); scrollToTarget(200, scrollableDiv); or // Scroll to targetElement var scrollableDiv = document.getElementById('scrollable_div'); var targetElement= document.getElementById('target_element'); scrollToTarget(targetElement, scrollableDiv);
given you have a div element you need to scroll inside, try this piece of code document.querySelector('div').scroll(x,y) this works with me inside a div with a scroll, this should work with you in case you pointed the mouse over this element and then tried to scroll down or up. If it manually works, it should work too
User Animated Scrolling Here's an example of how to programmatically scroll a <div> horizontally, without JQuery. To scroll vertically, you would replace JavaScript's writes to scrollLeft with scrollTop, instead. JSFiddle https://jsfiddle.net/fNPvf/38536/ HTML <!-- Left Button. --> <div style="float:left;"> <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. --> <input type="button" value="«" style="height: 100px;" onmousedown="scroll('scroller',3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/> </div> <!-- Contents to scroll. --> <div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;"> <!-- <3 --> <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" /> </div> <!-- Right Button. --> <div style="float:left;"> <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) --> <input type="button" value="»" style="height: 100px;" onmousedown="scroll('scroller',-3, 10);" onmouseup="clearTimeout(TIMER_SCROLL);"/> </div> JavaScript // Declare the Shared Timer. var TIMER_SCROLL; /** Scroll function. #param id Unique id of element to scroll. #param d Amount of pixels to scroll per sleep. #param del Size of the sleep (ms).*/ function scroll(id, d, del){ // Scroll the element. document.getElementById(id).scrollLeft += d; // Perform a delay before recursing this function again. TIMER_SCROLL = setTimeout("scroll('"+id+"',"+d+", "+del+");", del); } Credit to Dux. Auto Animated Scrolling In addition, here are functions for scrolling a <div> fully to the left and right. The only thing we change here is we make a check to see if the full extension of the scroll has been utilised before making a recursive call to scroll again. JSFiddle https://jsfiddle.net/0nLc2fhh/1/ HTML <!-- Left Button. --> <div style="float:left;"> <!-- (1) Whilst it's pressed, increment the scroll. When we release, clear the timer to stop recursive scroll calls. --> <input type="button" value="«" style="height: 100px;" onclick="scrollFullyLeft('scroller',3, 10);"/> </div> <!-- Contents to scroll. --> <div id="scroller" style="float: left; width: 100px; height: 100px; overflow: hidden;"> <!-- <3 --> <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" alt="image large" style="height: 100px" /> </div> <!-- Right Button. --> <div style="float:left;"> <!-- As (1). (Use a negative value of 'd' to decrease the scroll.) --> <input type="button" value="»" style="height: 100px;" onclick="scrollFullyRight('scroller',3, 10);"/> </div> JavaScript // Declare the Shared Timer. var TIMER_SCROLL; /** Scroll fully left function; completely scrolls a <div> to the left, as far as it will go. #param id Unique id of element to scroll. #param d Amount of pixels to scroll per sleep. #param del Size of the sleep (ms).*/ function scrollFullyLeft(id, d, del){ // Fetch the element. var el = document.getElementById(id); // Scroll the element. el.scrollLeft += d; // Have we not finished scrolling yet? if(el.scrollLeft < (el.scrollWidth - el.clientWidth)) { TIMER_SCROLL = setTimeout("scrollFullyLeft('"+id+"',"+d+", "+del+");", del); } } /** Scroll fully right function; completely scrolls a <div> to the right, as far as it will go. #param id Unique id of element to scroll. #param d Amount of pixels to scroll per sleep. #param del Size of the sleep (ms).*/ function scrollFullyRight(id, d, del){ // Fetch the element. var el = document.getElementById(id); // Scroll the element. el.scrollLeft -= d; // Have we not finished scrolling yet? if(el.scrollLeft > 0) { TIMER_SCROLL = setTimeout("scrollFullyRight('"+id+"',"+d+", "+del+");", del); } }
This is what has finally served me /** Set parent scroll to show element * #param element {object} The HTML object to show * #param parent {object} The HTML object where the element is shown */ var scrollToView = function(element, parent) { //Algorithm: Accumulate the height of the previous elements and add half the height of the parent var offsetAccumulator = 0; parent = $(parent); parent.children().each(function() { if(this == element) { return false; //brake each loop } offsetAccumulator += $(this).innerHeight(); }); parent.scrollTop(offsetAccumulator - parent.innerHeight()/2); }
I needed to scroll a dynamically loading element on a page so my solution was a little more involved. This will work on static elements that are not lazy loading data and data being dynamically loaded. const smoothScrollElement = async (selector: string, scrollBy = 12, prevCurrPos = 0) => { const wait = (timeout: number) => new Promise(resolve => setTimeout(resolve, timeout)); const el = document.querySelector(selector) as HTMLElement; let positionToScrollTo = el.scrollHeight; let currentPosition = Math.floor(el.scrollTop) || 0; let pageYOffset = (el.clientHeight + currentPosition); if (positionToScrollTo == pageYOffset) { await wait(1000); } if ((prevCurrPos > 0 && currentPosition <= prevCurrPos) !== true) { setTimeout(async () => { el.scrollBy(0, scrollBy); await smoothScrollElement(selector, scrollBy, currentPosition); }, scrollBy); } };
browser does scrolling automatically to an element that gets focus, so what you can also do it to wrap the element that you need to be scrolled to into <a>...</a> and then when you need scroll just set the focus on that a