Home > front end >  Why does my forloop and querySelectorAll dont work with multiple div's of the same class?
Why does my forloop and querySelectorAll dont work with multiple div's of the same class?

Time:07-23

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.

  1. don't use variable i in a function inside a loop. variables are all refrence.
  2. don't use arrow function to access this pointer of current element
  3. change your wrapper[i] in onDrag to this like above
  4. add user-select:none inside the .panel to be not selectable in css
  5. 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 in onDrag is a NodeList, not an HTMLElement. onDrag must operate on a specific element of wrapper, rather than the whole collection.
  • since i isn't block-local and is updated in the loop, it will be the last value i takes (one past the loop end condition); wrapper[i] in the mouse event handlers will only ever refer to one thing. Declaring i with let will make it block-local, and wrapper[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>

  • Related