I have a div that has overflow: scroll and I have some elements inside the DIV that are hidden. On click of a button on the page, I want to make the DIV scroll to a particular element inside the DIV.
How do I achieve this?
You need to read the offsetTop property of the div you need to scroll to and then set that offset to the scrollTop property of the container div. Bind this function the event you want to :
function scrollToElementD(){
var topPos = document.getElementById('inner-element').offsetTop;
document.getElementById('container').scrollTop = topPos-10;
}
div {
height: 200px;
width: 100px;
border: 1px solid black;
overflow: auto;
}
p {
height: 80px;
background: blue;
}
#inner-element {
background: red;
}
<div id="container"><p>A</p><p>B</p><p>C</p><p id="inner-element">D</p><p>E</p><p>F</p></div>
<button onclick="scrollToElementD()">SCROLL TO D</button>
function scrollToElementD(){
var topPos = document.getElementById('inner-element').offsetTop;
document.getElementById('container').scrollTop = topPos-10;
}
Fiddle : http://jsfiddle.net/p3kar5bb/322/ (courtesy #rofrischmann)
Just improved it by setting a smooth auto scrolling inside a list contained in a div
https://codepen.io/rebosante/pen/eENYBv
var topPos = elem.offsetTop
document.getElementById('mybutton').onclick = function () {
console.log('click')
scrollTo(document.getElementById('container'), topPos-10, 600);
}
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
I guess it may help someone :)
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);
You need a ref to the div you wish to scroll to inner-div and a ref to the scrollable div scrollable-div:
const scrollToDiv = () => {
const innerDivPos = document.getElementById('inner-div').offsetTop
document
.getElementById('scrollable-div')
.scrollTo({ top: innerDivPos, behavior: 'smooth' })
}
<div id="scrollable-div" style="height:100px; overflow-y:auto;">
<button type="button" style="margin-bottom:500px" onclick="scrollToDiv()">Scroll To Div</button>
<div id="inner-div">Inner Div</div>
</div>
Related
I added this code to my WordPress based website to make its front page horizontal. But it's not smooth and I cannot add scroll snap or anchor points. Can you help me about these? My website is https://kozb.art
<script type="text/javascript">
function replaceVerticalScrollByHorizontal( event ) {
if ( event.deltaY !== 0 ) {
window.scroll(window.scrollX + event.deltaY * 2, window.scrollY );
event.preventDefault();
}
}
const mediaQuery = window.matchMedia( '(min-width: 770px)' );
if ( mediaQuery.matches ) {
window.addEventListener( 'wheel', replaceVerticalScrollByHorizontal );
}
</script>
Edit: Here's my CSS code to make front page horizontal:
.elementor-section-wrap{
display: inline-flex;
}
.elementor-section{
width:100vw;
}
body{
overflow-y: hidden;
overscroll-behavior-y: none;
}
#media (max-width:768px){
.elementor-section-wrap{
display: block;
}
body{
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior-x: none;
}
}
You need animation knowledge to move the horizontal scroll smoothly. Let's start with a horizontal scrolling environment.
// index.html
...
<head>
<link href="index.css" rel="stylesheet">
</head>
<body>
<div id="screen">
<div id="content1"></div><div id="content2"></div><div id="content3"></div><div id="content4"></div>
</div>
<script src="./main.js"></script>
</body>
...
/* index.css */
body {
margin: 0;
width: 100vw;
height: 100vh;
overflow-y: hidden;
}
#screen {
white-space: nowrap;
height: 100%;
}
#screen > div {
width: 100vw;
height: 100vh;
display: inline-block;
}
#screen > div:nth-child(1) {
background: aqua;
}
#screen > div:nth-child(2) {
background: blueviolet
}
#screen > div:nth-child(3) {
background: chocolate
}
#screen > div:nth-child(4) {
background: darkolivegreen;
}
A web page has now been created that has four sections and occupies one screen size per section. For smooth, snap horizontal scroll applications, let's think step by step about what we need to make code for animation.
To implement Snap, you need to know what value scroll X should move to. In the current layout, the value is the offsetLeft value of the section element. And the section size changes depending on the browser size. So the code to get the offsetLeft of the section can be created as follows:
let sectionAnchorPointer = [];
const resizeHandler = () => {
const content1 = document.getElementById('content1');
const content2 = document.getElementById('content2');
const content3 = document.getElementById('content3');
const content4 = document.getElementById('content4');
sectionAnchorPointer = [content1.offsetLeft, content2.offsetLeft, content3.offsetLeft, content4.offsetLeft];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
resizeHandler();
});
In order to store the section offsetLeft value from the beginning, a function was executed to update the section offsetLeft when DOMContentLoaded occurred. If you want to make it more efficient, please apply debounce to the resize event handler.
Next, when the wheel occurs, find the section to move. In order to find the section to be moved, it is necessary to determine where the section is located and then perform the calculation according to the direction of the wheel. The code is as follows:
let nextSectionIndex = 0;
const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
const scrollX = Math.ceil(window.scrollX); // Fixed a bug where scrollX was decimalized
const rightValue = array[i + 1] ?? Infinity;
return leftValue <= scrollX && scrollX < rightValue;
});
window.addEventListener('wheel', ({ deltaY }) => {
const currentSectionIndex = getCurrentSectionIndex();
const add = Math.abs(deltaY) / deltaY;
nextSectionIndex = currentSectionIndex + add;
nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
console.log(sectionAnchorPointer[nextSectionIndex]);
});
To save the scroll position when accessing the page, Call the function when a DOMContentLoaded event occurs.
...
addEventListener('DOMContentLoaded', () => {
resizeHandler();
nextSectionIndex = getCurrentSectionIndex();
});
...
Next, apply the animation so that it slowly changes to the offsetLeft value of the section that needs to move the current scrollX value. For ease of understanding, let's make it a linear animation without acceleration. The code is as follows:
const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// linear animtion
if (Math.abs(window.scrollX - nextScrollX) > SCROLL_SPEED) {
const val = -Math.abs(window.scrollX - nextScrollX) / (window.scrollX - nextScrollX);
window.scroll(window.scrollX + val * SCROLL_SPEED, window.scrollY);
} else {
window.scroll(nextScrollX, window.scrollY);
}
requestAnimationFrame(scroll);
});
To apply dynamic animation by adding acceleration, add the following code instead of the code above.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// curve animation
if (Math.abs(window.scrollX - nextScrollX) > 1) {
let val = (nextScrollX - window.scrollX) / 8; // You can change 8 to another value to adjust the animation speed.
val = val > 0 ? Math.max(val, 1) : Math.min(val, -1);
window.scroll(window.scrollX + val, window.scrollY);
} else {
window.scroll(nextScrollX, window.scrollY);
}
requestAnimationFrame(scroll);
});
This is a simple example of implementation for understanding. Here is a link to the project using the code. If you are interested, please refer to the following link to understand Javascript animation. For your information, it would be more convenient to make an animation using a known library such as anime.js or popmotion.
This is the script code that fits your structure. Remove the existing wheel listener and insert this content.
const wrap = document.querySelectorAll('.elementor-section-wrap')[1]
let sectionAnchorPointer = [];
let resultX = 0;
const resizeHandler = () => {
sectionAnchorPointer = [...new Set([...wrap.children].map(el => el.offsetLeft))];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
resizeHandler();
nextSectionIndex = getCurrentSectionIndex();
});
resizeHandler();
let nextSectionIndex = 0;
const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
const scrollX = Math.ceil(resultX); // Fixed a bug where scrollX was decimalized
const rightValue = array[i + 1] ?? Infinity;
return leftValue <= resultX && resultX < rightValue;
});
window.addEventListener('wheel', (ev) => {
const {deltaY} = ev;
const currentSectionIndex = getCurrentSectionIndex();
const add = Math.abs(deltaY) / deltaY;
nextSectionIndex = currentSectionIndex + add;
nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
console.log(sectionAnchorPointer[nextSectionIndex]);
});
const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// linear animtion
if (Math.abs(resultX - nextScrollX) > SCROLL_SPEED) {
const val = -Math.abs(resultX - nextScrollX) / (resultX - nextScrollX);
resultX = resultX + val * SCROLL_SPEED;
} else {
resultX = nextScrollX;
}
window.scroll(resultX , 0);
requestAnimationFrame(scroll);
});
I have function that every time when I click on canvas div with image get created on that place where I clicked.
$(function () {
$("#the-canvas").click(function (e) {
x = e.pageX;
y = e.pageY;
var xMax = 2900;
var yMax = 2900;
var img = $('<img class="comment" src="indeksiraj-1.png" alt="myimage" />');
window.divMark = document.createElement("div");
divMark.classList = `markers mark`;
$(divMark).css({
position: "absolute",
left: x + "px",
top: y + "px",
});
$(divMark).append(img);
$(marksCanvas).append(divMark);
counter++;
saveLocalPos(x);
saveLocalY(y);
var imgHeight = img.outerHeight();
if (y + imgHeight > yMax) {
divMark.css("top", yMax - imgHeight);
}
var imgWidth = img.outerWidth();
if (x + imgWidth > xMax) {
divMark.css("left", yMax - imgWidth);
}
});
This is my delete function
deleteBtn.onclick = function (e) {
divMark.remove();
};
The problem of this function is that when I click on delete button it delete just one div but then I click again it those don't won't delete again.
Consider the following example.
$(function() {
var divMarks = [];
function addImage(props, event, target) {
var divMark = $("<div>", {
class: "markers mark"
});
if (target !== undefined) {
divMark.appendTo(target);
}
$("<img>", props).appendTo(divMark);
divMark.css({
position: "absolute",
top: (event.pageY - 10),
left: (event.pageX - 10)
});
return divMark;
}
$("#the-canvas").click(function(event) {
divMarks.push(addImage({
class: "comment",
src: "indeksiraj-1.png",
alt: "Image"
}, event, this));
});
})
#the-canvas {
border: 1px solid black;
width: 2900px;
height: 2900px;
}
.markers {
border: 1px solid red;
width: 20px;
height: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="the-canvas">
</div>
You know have an Array of the markers and you can remove them as needed. E.G.:
divMarks[2].remove();
Your code is only saving a reference to the last created divMark.
Each time you create a new one, it overwrites the old window.DivMark with the new one.
When you press the delete button, it accesses the last created divMark, removed it and on subsequent calls it tries to remove it again and again l.
Instead, you need to create an array and add a divMark to that array each time one is created. Then each time you press the delete button you need to get a divMark from the array, call the remove() and also remove it from the array
Do you have one button for each div or one button that deletes all the divs in a Last In First Out ?
// at the top of the files
const divsList = [];
const newId = (function () { let id = 0; return () => ++id; })();
// where you create the div
const div = ... newdiv
const currentId = newId();
div.setAttribute("id", newId());
divsList.push(currentId);
// delete handler
deleteBtn.onclick = function (e) {
const currentId = divsList[divsList.length - 1];
if (currentId !== undefined) {
document.getElementById(currentId).delete();
divsList.slice(0, -1);
}
};
I made this pen to simulate the issue.
function createBox() {
var box = document.createElement("div");
box.className = "box";
box.style.backgroundColor = "#"+((1<<24)*Math.random()|0).toString(16);
return box;
}
function prependInnerHTML() {
console.log('prependInnerHTML');
var element = document.getElementById('scroll');
var box = createBox();
element.innerHTML = box.outerHTML + element.innerHTML;
element.prepend(box);
}
function prependPrepend() {
console.log('prependPrepend');
var element = document.getElementById('scroll');
var box = createBox();
element.prepend(box);
}
function prependPrepend() {
console.log('prependPrepend');
var element = document.getElementById('scroll');
var box = createBox();
element.prepend(box);
}
function prependInsertBefore() {
console.log('prependInsertBefore');
var element = document.getElementById('scroll');
var box = createBox();
element.insertBefore(box, element.firstChild);
}
function scroll() {
console.log('scroll');
var detailElement = document.getElementById('details');
var scrollElement = document.getElementById('scroll');
detailElement.innerHTML = "ScrollTop: " + scrollElement.scrollTop;
}
function clearScrollElement() {
console.log('clear');
var element = document.getElementById('scroll');
while(element.firstChild){
element.removeChild(element.firstChild);
}
}
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%
}
#details {
position: fixed;
top: 10px;
left: 20px;
background-color: white;
}
#options {
position: fixed;
top: 10px;
right: 20px;
background-color: white;
}
#scroll {
overflow-x: hidden;
overflow-y: scroll;
height: 100%;
width: 100%;
}
.box {
height: 100vh;
width: 100%
}
<div id="options">
<span onclick="prependInnerHTML()">InnerHTML</span>
<span onclick="prependInsertBefore()">InsertBefore</span>
<span onclick="prependPrepend()">Prepend</span>
<span onclick="clearScrollElement()">Clear</span>
</div>
<div id="details">
ScrollTop: 0
</div>
<div id="scroll" onscroll="scroll()"></div>
The issue is that when a element gets prepended to a scrollable element, it will have different behaviors across different browsers.
innerHTML:
The first prepend method is changing the innerHTML of the scrollable element. This seems to be consistent across multiple browsers.
The problem is that frameworks like Vue don't internally use this method, it probably uses one of the other methods.
InsertBefore with Chrome:
If the scrollTop is 0 and we prepend the scrollTop stays zero. If the scrollTop is higher than zero it will adjust the scrollTop to include the height of the prepended element.
InsertBefore with IE/Edge/Firefox:
Same behavior as the innerHTML one.
Prepend:
Prepend seems te be unsupported on IE/Edge so i'll skip that one.
Question:
How do I make the InsertBefore behave the same across all the browsers, without introducing all kinds of browser checks?
Try doing this
/**
* Adds a new element to the top of the container, If after is a valid element then the new element is added after that element
* #param {HTMLElement} newItem - newItem to be added
* #param {HTMLElement} container - container
* #param {HTMLElement} [after=] - optional parameter, states that the new item will be added after this element
* #returns {HTMLElement}
*/
function addElementToTop(newItem, container, after) {
if (after instanceof HTMLElement === false || after.parentElement !== container) {
var first = container.firstElementChild;
} else {
first = after.nextElementSibling;
}
if (first !== null) {
container.insertBefore(newItem, first);
} else {
container.appendChild(newItem);
}
return newItem;
}
Here is the simple version
function addElementToTop(newItem, container) {
var first = container.firstElementChild;
if (first) container.insertBefore(newItem, first);
else container.appendChild(newItem);
return newItem;
}
The below version maintains the same view-port but the scroll changes
function addElementToTopSteadyScroll(newItem, container) {
var bot = container.scrollHeight - container.scrollTop;
var first = container.firstElementChild;
if (first) container.insertBefore(newItem, first);
else container.appendChild(newItem);
container.scrollTop = container.scrollHeight - bot;
return newItem;
}
Whereas the below version maintains the same scroll but the view-port changes
function addElementToTopFlowScroll(newItem, container) {
var top = container.scrollTop;
var first = container.firstElementChild;
if (first) container.insertBefore(newItem, first);
else container.appendChild(newItem);
container.scrollTop = top;
return newItem;
}
I'm trying to build a draggable column based layout in JavaScript and having a bit of hard time with it.
The layout comprises of 3 columns (divs), with two dragable divs splitting each. The idea is that they are positioned absolutely and as you drag the draggers, the columns' respective widths, and left values are updated.
The three columns should always span the full width of the browser (the right most column is 100% width), but the other two should remain static by default when the browser is resized (which is why i'm using px, not %).
My code isn't working as of yet, I'm relatively new to JavaScript (which is why I don't want to use jQuery).
Having said that, there must be a more efficient (and cleaner) way of achieving this with less code that works (without reaching for the $ key).
If anyone with some awesome JS skills can help me out on this I'd be super-appreciative.
Here's the fiddle I'm working on http://jsfiddle.net/ZFwz5/3/
And here's the code:
HTML
<!-- colums -->
<div class="col colA"></div>
<div class="col colB"></div>
<div class="col colC"></div>
<!-- draggers -->
<div class="drag dragA" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left:100px;"><div></div></div>
<div class="drag dragB" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left: 300px;"><div></div></div>
CSS:
body {
overflow:hidden;
}
.col {
position: absolute;
height:100%;
left: 0;
top: 0;
overflow: hidden;
}
.colA {background:red;width:100px;}
.colB {background:green; width:200px; left:100px;}
.colC {background:blue; width:100%; left:300px;}
.drag > div {
background: 0 0;
position: absolute;
width: 10px;
height: 100%;
cursor: col-resize;
left: -5px;
}
and my terrible JavaScript:
//variabe columns
var colA = document.querySelector('.colA');
var colB = document.querySelector('.colB');
var colC = document.querySelector('.colC');
//variable draggers
var draggers = document.querySelectorAll('.drag');
var dragA = document.querySelector(".dragA");
var dragB = document.querySelector(".dragB");
var dragging = false;
function drag() {
var dragLoop;
var t = this;
var max;
var min;
if (dragging = true) {
if (this == dragA) {
min = 0;
max = dragB.style.left;
} else {
min = dragA.style.left;
max = window.innerWidth;
}
dragLoop = setInterval(function () {
var mouseX = event.clientX;
var mouseY = event.clientY;
if (mouseX >= max) {
mouseX = max;
}
if (mouseY <= min) {
mouseY = min;
}
t.style.left = mouseX;
updateLayout();
}, 200);
}
}
function updateLayout() {
var posA = dragA.style.left;
var posB = dragB.style.left;
colB.style.paddingRight = 0;
colA.style.width = posA;
colB.style.left = posA;
colB.style.width = posB - posA;
colC.style.left = posB;
colC.style.width = window.innerWidth - posB;
}
for (var i = 0; i < draggers.length; i++) {
draggers[i].addEventListener('mousedown', function () {
dragging = true;
});
draggers[i].addEventListener('mouseup', function () {
clearInterval(dragLoop);
dragging = false;
});
draggers[i].addEventListener('mouseMove', function () {
updateLayout();
drag();
});
}
I see a couple of things wrong here. First of all, the mousemove event only fires on an element when the mouse is over that element. You might have better luck registering a mousemove listener on the parent of your div.drag elements, then calculating the mouse's position inside that parent whenever a mouse event happens, then using that position to resize your columns and your draggers.
Second, I'm not quite sure what you're trying to do by registering a function with setInterval. You're doing pretty well with registering event listeners; why not continue to use them to change the state of your DOM? Why switch to a polling-based mechanism? (and the function you pass to setInterval won't work anyway - it refers to a variable named event, which in that context is undefined.)
This is just a little example... I hope it can help you :)
window.onload = function() {
var myDiv = document.getElementById('myDiv');
function show_coords(){
var monitor = document.getElementById('monitor');
var x = event.clientX - myDiv.clientWidth / 2;
var y = event.clientY - myDiv.clientWidth / 2;
monitor.innerText = "X: " + x + "\n" + "Y: " + y;
myDiv.style.left = x + "px";
myDiv.style.top = y + "px";
}
document.onmousemove = function(){
if(myDiv.innerText == "YES"){show_coords();}
}
myDiv.onmousedown = function(){
myDiv.innerText = "YES";
}
myDiv.onmouseup = function(){
myDiv.innerText = "NO";
}
}
I want to use the 'mouse's drag' to drag a background's position around, inside a box.
The CSS:
.filmmakers #map {
width : 920px;
height : 500px;
margin-top : 50px;
margin-left : 38px;
border : 1px solid rgb(0, 0, 0);
cursor : move;
overflow : hidden;
background-image : url('WorldMap.png');
background-repeat : no-repeat;
}
The html:
<div id = "map" src = "WorldMap.png" onmousedown = "MouseMove(this)"> </div>
The Javascript:
function MouseMove (e) {
var x = e.clientX;
var y = e.clientY;
e.style.backgroundPositionX = x + 'px';
e.style.backgroundPositionY = y + 'px';
e.style.cursor = "move";
}
Nothing happens, no errors, no warnings, nothing... I have tried lots of things: an absolutely positioned image inside a div (you can guess why that didn't work), A draggable div inside a div with a background image, a table with drag and drop, and finally I tried this:
function MouseMove () {
e.style.backgroundPositionX = 10 + 'px';
e.style.backgroundPositionY = 10 + 'px';
e.style.cursor = "move";
}
This works, but its not relative to the mouse's position, pageX and pageY don't work either.
A live demo: http://jsfiddle.net/VRvUB/224/
P.S: whatever your idea is, please don't write it in JQuery
From your question I understood you needed help implementing the actual "dragging" behavior. I guess not. Anyway, here's the results of my efforts: http://jsfiddle.net/joplomacedo/VRvUB/236/
The drag only happens when the mouse button, and.. well, it behaves as I think you might want it to. Just see the fiddle if you haven't =)
Here's the code for those who want to see it here:
var AttachDragTo = (function () {
var _AttachDragTo = function (el) {
this.el = el;
this.mouse_is_down = false;
this.init();
};
_AttachDragTo.prototype = {
onMousemove: function (e) {
if ( !this.mouse_is_down ) return;
var tg = e.target,
x = e.clientX,
y = e.clientY;
tg.style.backgroundPositionX = x - this.origin_x + this.origin_bg_pos_x + 'px';
tg.style.backgroundPositionY = y - this.origin_y + this.origin_bg_pos_y + 'px';
},
onMousedown: function(e) {
this.mouse_is_down = true;
this.origin_x = e.clientX;
this.origin_y = e.clientY;
},
onMouseup: function(e) {
var tg = e.target,
styles = getComputedStyle(tg);
this.mouse_is_down = false;
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
},
init: function () {
var styles = getComputedStyle(this.el);
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
//attach events
this.el.addEventListener('mousedown', this.onMousedown.bind(this), false);
this.el.addEventListener('mouseup', this.onMouseup.bind(this), false);
this.el.addEventListener('mousemove', this.onMousemove.bind(this), false);
}
};
return function ( el ) {
new _AttachDragTo(el);
};
})();
/*** IMPLEMENTATION ***/
//1. Get your element.
var map = document.getElementById('map');
//2. Attach the drag.
AttachDragTo(map);
This isn't working because you are passing the element "map" to your MouseMove function, and using it as both an event object and an element. You can fix this painlessly by using JavaScript to assign your event handler rather than HTML attributes:
<div id="map"></div>
And in your JavaScript:
document.getElementById('map').onmousemove = function (e) {
// the first parameter (e) is automatically assigned an event object
var x = e.clientX;
var y = e.clientY;
// The context of this is the "map" element
this.style.backgroundPositionX = x + 'px';
this.style.backgroundPositionY = y + 'px';
}
http://jsfiddle.net/VRvUB/229/
The downside of this approach is that the backgroundPositionX and backgroundPositionY style properties are not supported in all browsers.
You mention "an absolutely positioned image inside a div" which is probably the more compatible solution for this. To make this setup work, you need to set the position of the outer element to relative, which makes absolute child elements use its bounds as zero.
<div id="map">
<img src="" alt="">
</div>
CSS:
#map {
position:relative;
overflow:hidden;
}
#map img {
position:absolute;
left:0;
top:0;
}
Here it is applied to your code: http://jsfiddle.net/VRvUB/232/
This works 100%
Vanilla Javascript
document.getElementById('image').onmousemove = function (e) {
var x = e.clientX;
var y = e.clientY;
this.style.backgroundPositionX = -x + 'px';
this.style.backgroundPositionY = -y + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/vhL5kH2/image-14.png)';
}
document.getElementById('image').onmouseleave = function (e) {
this.style.backgroundPositionX = 0 + 'px';
this.style.backgroundPositionY = 0 + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/Ph9MCB2/template.png)';
}
.container {
max-width: 670px;
height: 377px;
}
#image {
max-width: 670px;
height: 377px;
cursor: crosshair;
overflow: hidden;
background-image: url('https://i.ibb.co/Ph9MCB2/template.png');
background-repeat: no-repeat;
}
<div class="container">
<div id="image">
</div>
</div>