Basically I'm trying to make multiple draggable div's with a script. It works fine for 1 div like this:
https://jsfiddle.net/werwer/3zvqm2sa/1/
where I have aquerySelector that gets one class name.
I wish to make multiple draggable divs, sort of like a windows computer. So used a querySelectorAll, put my eventListeners into a forloop to loop through the array, and selected the array item one by one like this:
https://jsfiddle.net/werwer/0n5eLmfv/11/
It completely removed the ability to move all the divs (including the one that originally can move). So I placed a console.log to check if my mouse up and down works for both divs. Here's the fun part: the mouseup and down check worked for the original black div (the one that supposedly can move), but could only detect mouse up for the new white div. So I'm not exactly sure what is going on.
var wrapper = document.querySelectorAll(".panel");
for (i = 0; i < wrapper.length - 1; i ) {
/* multiple event listeners for mouse up and down */
wrapper[i].addEventListener("mousedown", () => {
wrapper[i].classList.add("active");
wrapper[i].addEventListener("mousemove", onDrag);
console.log("check mouse down"); /* checking mouse down */
});
document.addEventListener("mouseup", () => {
wrapper[i].classList.remove("active");
wrapper[i].removeEventListener("mousemove", onDrag);
console.log("check mouse up"); /* checking mouse down */
});
}
/* what moves the divs */
function onDrag({ movementX, movementY }) {
let getStyle = window.getComputedStyle(wrapper);
let leftVal = parseInt(getStyle.left);
let topVal = parseInt(getStyle.top);
wrapper.style.left = `${leftVal movementX}px`;
wrapper.style.top = `${topVal movementY}px`;
}
.projects {
display: flex;
width: 100%;
height: 125vh;
background-image: url(images/laptop.png);
background-position: 100%;
background-position-y: center;
background-position-x: center;
background-size: 95%;
background-repeat: no-repeat;
}
.gamedev-panel {
position: absolute;
top: 3rem;
left: 16rem;
width: 40rem;
height: 30rem;
background-color: #403d39;
}
.gamedev-titlebar {
height: 3rem;
background: #EB5E28;
display: flex;
align-items: center;
justify-content: right;
}
.btn-1 {
background: turquoise;
}
.btn-2 {
background: yellow;
}
.btn-3 {
background: red;
}
.game-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-right: 1rem;
}
.gamedev-heading {
background: none;
position: absolute;
/* width: 369.43px;
height: 40.32px;
left: 408.26px;
top: 2592.77px; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #CCC5B9;
pointer-events: none;
}
.gamedev-gif {
position: absolute;
bottom: 1rem;
right: 1rem;
pointer-events: none;
}
.webdev-panel {
position: absolute;
top: 1rem;
right: 16rem;
width: 25rem;
height: 30rem;
background-color: #fffcf2;
display: inline-flex;
justify-content: flex-end;
overflow: hidden;
}
.webdev-titlebar {
/* position: absolute; */
height: 30rem;
width: 3rem;
background: #EB5E28;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
pointer-events: none;
}
.web-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-top: 1rem;
/* margin-right: 1rem; */
pointer-events: none;
}
.webdev-heading {
background: none;
/* position: absolute;
right: 5rem; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #252422;
pointer-events: none;
}
.webdev-content {
margin-right: 1rem;
pointer-events: none;
}
.webdev-img {
width: 80%;
height: 80%;
position: absolute;
right: 4rem;
top: 5rem;
pointer-events: none;
<div >
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
<h2 >I DEVELOP GAMES</h2>
<img src="images/game.gif" alt="" >
</div>
<div >
<div >
<h2 >I MAKE WEBSITES</h2>
<img src="images/web.png" alt="" >
</div>
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
<div ></div>
</div>
CodePudding user response:
your code needs some changes.
- don't use variable
i
in a function inside a loop. variables are all refrence. - don't use arrow function to access
this
pointer of current element - change your
wrapper[i]
inonDrag
tothis
like above - add
user-select:none
inside the.panel
to be not selectable in css - add mouse out event to current element for the time the mouse jumped out of the screen and the
mouseup
doesn't fire
var wrapper = document.querySelectorAll(".panel");
for (i = 0; i < wrapper.length; i ) {
wrapper[i].addEventListener("mousedown",function () {
this.classList.add("active");
this.addEventListener("mousemove", onDrag);
});
wrapper[i].addEventListener("mouseup",function () {
this.classList.remove("active");
this.removeEventListener("mousemove", onDrag);
});
wrapper[i].addEventListener("mouseout",function () {
this.removeEventListener("mousemove", onDrag);
});
}
function onDrag({
movementX,
movementY
}) {
let getStyle = window.getComputedStyle(this);
let leftVal = parseInt(getStyle.left);
let topVal = parseInt(getStyle.top);
this.style.left = `${leftVal movementX}px`;
this.style.top = `${topVal movementY}px`;
}
.panel {
user-select: none;
}
.projects {
display: flex;
width: 100%;
height: 125vh;
background-image: url(images/laptop.png);
background-position: 100%;
background-position-y: center;
background-position-x: center;
background-size: 95%;
background-repeat: no-repeat;
}
.gamedev-panel {
position: absolute;
top: 3rem;
left: 16rem;
width: 40rem;
height: 30rem;
background-color: #403d39;
}
.gamedev-titlebar {
height: 3rem;
background: #EB5E28;
display: flex;
align-items: center;
justify-content: right;
}
.btn-1 {
background: turquoise;
}
.btn-2 {
background: yellow;
}
.btn-3 {
background: red;
}
.game-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-right: 1rem;
}
.gamedev-heading {
background: none;
position: absolute;
/* width: 369.43px;
height: 40.32px;
left: 408.26px;
top: 2592.77px; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #CCC5B9;
pointer-events: none;
}
.gamedev-gif {
position: absolute;
bottom: 1rem;
right: 1rem;
pointer-events: none;
}
.webdev-panel {
position: absolute;
top: 1rem;
right: 16rem;
width: 25rem;
height: 30rem;
background-color: #fffcf2;
display: inline-flex;
justify-content: flex-end;
overflow: hidden;
}
.webdev-titlebar {
/* position: absolute; */
height: 30rem;
width: 3rem;
background: #EB5E28;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
pointer-events: none;
}
.web-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-top: 1rem;
/* margin-right: 1rem; */
pointer-events: none;
}
.webdev-heading {
background: none;
/* position: absolute;
right: 5rem; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #252422;
pointer-events: none;
}
.webdev-content {
margin-right: 1rem;
pointer-events: none;
}
.webdev-img {
width: 80%;
height: 80%;
position: absolute;
right: 4rem;
top: 5rem;
pointer-events: none;
}
<div >
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
<h2 >I DEVELOP GAMES</h2>
<img src="images/game.gif" alt="" >
</div>
<div >
<div >
<h2 >I MAKE WEBSITES</h2>
<img src="images/web.png" alt="" >
</div>
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
<div ></div>
</div>
CodePudding user response:
The upper bound of wrapper.length - 1
excludes the last .panel
.
Additionally, there are various issues with variable bindings:
wrapper
inonDrag
is aNodeList
, not an HTMLElement.onDrag
must operate on a specific element ofwrapper
, rather than the whole collection.- since
i
isn't block-local and is updated in the loop, it will be the last valuei
takes (one past the loop end condition);wrapper[i]
in the mouse event handlers will only ever refer to one thing. Declaringi
withlet
will make it block-local, andwrapper[i]
within the event handlers will then refer to the correct element.
Note that both the above binding issues are already covered by other questions on SO.
CodePudding user response:
Please delegate
Here is an untested version since I am not on a proper device, but it gives the structure
In the onDrag we just test the active element if there is none, we could leave, but as I said, I cannot actually test this to conclusion
I use the same eventlistener for mouseup and down and then instead of add or remove I toggle active depending on the event. If mousedown, add active if up remove active (that is what toggle does)
I add the event listeners to the OUTER div, that wraps ALL the panels, and in the event handler, the e.target is (possibly inside) the panel. So to get the panel itself, I use.closest to make sure we are acting on the wrapper
const wrapper = document.querySelector(".projects");
/* what moves the divs */
const onDrag = ({ movementX, movementY }) => {
const wrapperPanel = wrapper.querySelector(".panel.active")
let getStyle = window.getComputedStyle(wrapperPanel);
let leftVal = parseInt(getStyle.left);
let topVal = parseInt(getStyle.top);
wrapperPanel.style.left = `${leftVal movementX}px`;
wrapperPanel.style.top = `${topVal movementY}px`;
}
const mouse = (e) => {
const tgt = e.target.closest(".panel");
if (tgt) tgt.classList.toggle("active",e.type=="mousedown");
};
wrapper.addEventListener("mousedown", mouse)
wrapper.addEventListener("mouseup", mouse)
wrapper.addEventListener("mousemove", onDrag);
.projects {
display: flex;
width: 100%;
height: 125vh;
background-image: url(images/laptop.png);
background-position: 100%;
background-position-y: center;
background-position-x: center;
background-size: 95%;
background-repeat: no-repeat;
}
.gamedev-panel {
position: absolute;
top: 3rem;
left: 16rem;
width: 40rem;
height: 30rem;
background-color: #403d39;
}
.gamedev-titlebar {
height: 3rem;
background: #EB5E28;
display: flex;
align-items: center;
justify-content: right;
}
.btn-1 {
background: turquoise;
}
.btn-2 {
background: yellow;
}
.btn-3 {
background: red;
}
.game-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-right: 1rem;
}
.gamedev-heading {
background: none;
position: absolute;
/* width: 369.43px;
height: 40.32px;
left: 408.26px;
top: 2592.77px; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #CCC5B9;
pointer-events: none;
}
.gamedev-gif {
position: absolute;
bottom: 1rem;
right: 1rem;
pointer-events: none;
}
.webdev-panel {
position: absolute;
top: 1rem;
right: 16rem;
width: 25rem;
height: 30rem;
background-color: #fffcf2;
display: inline-flex;
justify-content: flex-end;
overflow: hidden;
}
.webdev-titlebar {
/* position: absolute; */
height: 30rem;
width: 3rem;
background: #EB5E28;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
pointer-events: none;
}
.web-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-top: 1rem;
/* margin-right: 1rem; */
pointer-events: none;
}
.webdev-heading {
background: none;
/* position: absolute;
right: 5rem; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #252422;
pointer-events: none;
}
.webdev-content {
margin-right: 1rem;
pointer-events: none;
}
.webdev-img {
width: 80%;
height: 80%;
position: absolute;
right: 4rem;
top: 5rem;
pointer-events: none;
<div >
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
<h2 >I DEVELOP GAMES</h2>
<img src="images/game.gif" alt="" >
</div>
<div >
<div >
<h2 >I MAKE WEBSITES</h2>
<img src="images/web.png" alt="" >
</div>
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
<div ></div>
</div>
CodePudding user response:
You're adding several mouseup
event listeners to your document
,you're iterating up to 2 indexes below the final length of your element array, and you're never declaring your i
variable.
In your event mousemove listener, you're using the wrapper
variable that you haven't redeclared from your previous code. You can use the target
property from the event
object to get the element calling your listener.
var wrapper = document.querySelectorAll(".panel");
for (let i = 0; i < wrapper.length; i ) {
/* multiple event listeners for mouse up and down */
wrapper[i].addEventListener("mousedown", () => {
wrapper[i].classList.add("active");
wrapper[i].addEventListener("mousemove", onDrag);
console.log("check mouse down"); /* checking mouse down */
});
function mouseMove() {
wrapper[i].classList.remove("active");
wrapper[i].removeEventListener("mousemove", mouseMove);
wrapper[i].removeEventListener("mousemove", onDrag);
console.log("check mouse up"); /* checking mouse down */
};
wrapper[i].addEventListener("mouseup", mouseMove);
}
/* what moves the divs */
function onDrag(event) {
let getStyle = window.getComputedStyle(event.target);
let leftVal = parseInt(getStyle.left);
let topVal = parseInt(getStyle.top);
event.target.style.left = `${leftVal event.movementX}px`;
event.target.style.top = `${topVal event.movementY}px`;
}
.projects {
display: flex;
width: 100%;
height: 125vh;
background-image: url(images/laptop.png);
background-position: 100%;
background-position-y: center;
background-position-x: center;
background-size: 95%;
background-repeat: no-repeat;
}
.gamedev-panel {
position: absolute;
top: 3rem;
left: 16rem;
width: 40rem;
height: 30rem;
background-color: #403d39;
}
.gamedev-titlebar {
height: 3rem;
background: #EB5E28;
display: flex;
align-items: center;
justify-content: right;
}
.btn-1 {
background: turquoise;
}
.btn-2 {
background: yellow;
}
.btn-3 {
background: red;
}
.game-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-right: 1rem;
}
.gamedev-heading {
background: none;
position: absolute;
/* width: 369.43px;
height: 40.32px;
left: 408.26px;
top: 2592.77px; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #CCC5B9;
pointer-events: none;
}
.gamedev-gif {
position: absolute;
bottom: 1rem;
right: 1rem;
pointer-events: none;
}
.webdev-panel {
position: absolute;
top: 1rem;
right: 16rem;
width: 25rem;
height: 30rem;
background-color: #fffcf2;
display: inline-flex;
justify-content: flex-end;
overflow: hidden;
}
.webdev-titlebar {
/* position: absolute; */
height: 30rem;
width: 3rem;
background: #EB5E28;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
pointer-events: none;
}
.web-btn {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
margin-top: 1rem;
/* margin-right: 1rem; */
pointer-events: none;
}
.webdev-heading {
background: none;
/* position: absolute;
right: 5rem; */
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-size: 36px;
line-height: 45px;
letter-spacing: 0.1em;
margin-left: 1rem;
color: #252422;
pointer-events: none;
}
.webdev-content {
margin-right: 1rem;
pointer-events: none;
}
.webdev-img {
width: 80%;
height: 80%;
position: absolute;
right: 4rem;
top: 5rem;
pointer-events: none;
<div >
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
<h2 >I DEVELOP GAMES</h2>
<img src="images/game.gif" alt="" >
</div>
<div >
<div >
<h2 >I MAKE WEBSITES</h2>
<img src="images/web.png" alt="" >
</div>
<div >
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
<div ></div>
</div>