Home > database >  How can I prevent a forEach function from firing on every instance of an object all at once?
How can I prevent a forEach function from firing on every instance of an object all at once?

Time:12-07

I've got these menus that open when the user hovers over an icon with lists inside the menus. The pointerout section of the code works fine (user hovers out of the menu and that menu closes and the icon returns to its initial, unhovered inactive state) but the pointerenter function is causing all of the menus to open at once regardless of which one I hover on.

Given that the exit event works fine, I'm guessing there's an issue with enter event that's causing all menus to open at once but I can't seem to pinpoint it. This isn't the first time I've used forEach for something so I'm guessing it's something inside of the function that isn't formatted properly.

const logos = (sel, par) => (par || document).querySelectorAll(sel);
const locations = (sel, par) => (par || document).querySelectorAll(sel);

const logoFn = logo => {
  logo.addEventListener('pointerenter', event => {
    event.target.classList.add('active');
    locations('.storeList').forEach(location => {
      location.classList.add('active');
    });
    locations('.storeList').forEach(location => {
      location.addEventListener('pointerleave', event => {
        logo.classList.remove('active');
        event.target.classList.remove('active');
      });
    });
  });
};

logos('.logoBox').forEach(logoFn);
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.landingPageContainer {
  display: flex;
  flex-direction: column;
  position: relative;
  width: 50vw;
  height: 25vw;
  background-color: slategrey;
  margin: 0 auto;
}

.oemLogosContainer {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 1vw;
  justify-content: flex-start;
  padding: 0 0.5vw 1vw 0.5vw;
  width: 100%;
  height: auto;
  margin: auto auto 0 auto;
}

.logoBox {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  width: 8vw;
  height: 8vw;
  border: 1px solid rgb(40, 40, 40);
  padding: 6px;
  border-radius: 5px;
  background-color: rgb(10, 10, 10, 0.9);
  box-shadow: -2px -1px 4px rgba(0, 0, 0, .7);
  transform: perspective(600px);
  transition: all 0.2s linear;
}

.logoBox:hover {
  cursor: pointer;
}

.logoBox::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, .15)), to(rgba(0, 0, 0, .25))), -webkit-gradient(linear, left top, right bottom, color-stop(0, rgba(255, 255, 255, 0)), color-stop(0.5, rgba(255, 255, 255, .1)), color-stop(0.501, rgba(255, 255, 255, 0)), color-stop(1, rgba(255, 255, 255, 0)));
  background: -moz-linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), -moz-linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  background: linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  opacity: 0;
  transition: opacity 0.2s linear;
}

.logoBox.active::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, .15)), to(rgba(0, 0, 0, .25))), -webkit-gradient(linear, left top, right bottom, color-stop(0, rgba(255, 255, 255, 0)), color-stop(0.5, rgba(255, 255, 255, .1)), color-stop(0.501, rgba(255, 255, 255, 0)), color-stop(1, rgba(255, 255, 255, 0)));
  background: -moz-linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), -moz-linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  background: linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  opacity: 1;
  transition: opacity 0.2s linear;
}

.logoBox.active {
  transform: perspective(600px) rotateY(35deg);
  box-shadow: -3px -1px 6px rgba(0, 0, 0, .7);
  transition: all 0.2s linear;
}

.logoBox img {
  object-fit: scale-down;
  height: 100%;
  width: 100%;
}

.storeList {
  display: flex;
  flex-direction: column;
  position: absolute;
  visibility: hidden;
  opacity: 0;
  margin: 0;
  width: 24vw;
  height: auto;
  border: 1px solid rgb(40, 40, 40);
  padding: 0.2vw;
  border-radius: 5px;
  background-color: rgb(10, 10, 10, 0.9);
  box-shadow: -2px -1px 4px rgba(0, 0, 0, .7);
  transition: all 0.2s linear;
}

.storeList ul,
a {
  text-decoration: none;
  list-style: none;
}

.storeList.active {
  position: absolute;
  visibility: visible;
  opacity: 1;
}

.chryslerLocations {
  bottom: 5vw;
  left: 1vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, visibility 0.2s linear;
}

.chryslerLocations.active {
  bottom: 10vw;
  left: 1vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, visibility 0.2s linear;
}

.dodgeLocations {
  bottom: 5vw;
  left: 10vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, z-index 0.2s linear;
}

.dodgeLocations.active {
  bottom: 10vw;
  left: 10vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, z-index 0.2s linear;
}

.jeepLocations {
  position: absolute;
  opacity: 0;
  bottom: 5vw;
  left: 19vw;
  transition: opacity 0.2s linear, bottom 0.2s linear;
}

.jeepLocations.active {
  bottom: 10vw;
  left: 19vw;
  transition: opacity 0.2s linear, bottom 0.2s linear;
}

.location {
  display: flex;
  flex-direction: row-reverse;
  flex-wrap: wrap;
  width: 100%;
  height: 25%;
  padding: 0.2vw;
  font-family: "DDC Font Face", "Open Sans", Arial, Helvetica, sans-serif;
  font-weight: bold;
  font-size: 1vw;
  letter-spacing: 0.03vw;
  border-radius: 5px;
  background-color: rgb(255, 8, 0, 0);
  transition: all 0.15s linear;
  color: #ff0800;
  align-items: center;
  justify-content: flex-end;
}

.location:hover {
  color: white;
  background-color: rgb(255, 8, 0, 0.7);
  transition: all 0.15s linear;
}

.locationName {
  width: 75%;
}

.location img {
  object-fit: scale-down;
  align-self: flex-start;
  width: 20%;
  height: 100%;
  margin: 2% 2% 0 0;
}

.locationSubInfo {
  display: flex;
  flex-direction: column;
  font-family: "DDC Font Face", "Open Sans", Arial, Helvetica, sans-serif;
  color: white;
  font-weight: normal;
  font-size: 0.7vw;
  margin-left: 22%;
}

.flexBreak {
  flex-basis: 100%;
  width: 0;
}
<body style="background-color: darkgrey;">
  <div >
    <nav >
      <!-- chrysler -->
      <div id="chryslerContainer" >
        <div id="chryslerLogoBox" >
          <img alt="Chrysler Logo" src="https://i.imgur.com/m33KwSY.png">
        </div>
        <div id="chryslerLocations" >
          <ul>
            <li id="chryslerLocation1">
              <a href="" ><span >Location Name</span><img alt="Chrysler Logo" src="https://i.imgur.com/m33KwSY.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <!-- dodge -->
      <div id="dodgeContainer" >
        <div id="dodgeLogoBox" >
          <img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
        </div>
        <div id="dodgeLocations" >
          <ul>
            <li id="dodgeLocation1">
              <a href="" ><span >Location Name</span><img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
            <li id="dodgeLocation2">
              <a href="" ><span >Location Name</span><img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <!-- jeep -->
      <div id="jeepContainer" >
        <div id="jeepLogoBox" >
          <img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
        </div>
        <div id="jeepLocations" >
          <ul>
            <li id="jeepLocation1">
              <a href="" ><span >Location Name</span><img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
            <li id="jeepLocation2">
              <a href="" ><span >Location Name</span><img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  </div>
</body>

CodePudding user response:

Your js code was adding event listeners to elements inside event listeners.

I splitted the two segments adding event listeners for pointerenter and pointerleave inside the function that was called for each .logobox.

Then I also made sure that function was called for the parent .logoContainer instead and that those two segments were taking care only of respectively adding and removing the class active. Since I changed the root element passed to the function, I explicitely also added/removed the class .active for the child .logoBox so that the macro menu was flipping/unflipping as well.

That's the only modification I made and it seems to work correctly now.

I also better tidied up the html by the way.

It's quite a tricky js approach I see used there.. and it's curious that those logos and locations function were crafted like that accepting a par argument (parent) that you were not using. I also made sure I was using that feature as well in your logic to make it meaningful.

const logos = (sel, par) => (par || document).querySelectorAll(sel);
const locations = (sel, par) => (par || document).querySelectorAll(sel);

const logoFn = logo => {

  logo.addEventListener('pointerenter', event => {
    event.target.classList.add('active');    
    event.target.querySelector('.logoBox').classList.add('active');
    locations('.storeList', event.target).forEach(location => {
      location.classList.add('active');
    });    
  });
  
 logo.addEventListener('pointerleave', event => {
    event.target.classList.remove('active');
    event.target.querySelector('.logoBox').classList.remove('active');
    locations('.storeList', event.target).forEach(location => {
      location.classList.remove('active');
    });    
  });
  
};

logos('.logoContainer').forEach(logoFn);
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.landingPageContainer {
  display: flex;
  flex-direction: column;
  position: relative;
  width: 50vw;
  height: 25vw;
  background-color: slategrey;
  margin: 0 auto;
}

.oemLogosContainer {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 1vw;
  justify-content: flex-start;
  padding: 0 0.5vw 1vw 0.5vw;
  width: 100%;
  height: auto;
  margin: auto auto 0 auto;
}

.logoBox {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  width: 8vw;
  height: 8vw;
  border: 1px solid rgb(40, 40, 40);
  padding: 6px;
  border-radius: 5px;
  background-color: rgb(10, 10, 10, 0.9);
  box-shadow: -2px -1px 4px rgba(0, 0, 0, .7);
  transform: perspective(600px);
  transition: all 0.2s linear;
}

.logoBox:hover {
  cursor: pointer;
}

.logoBox::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, .15)), to(rgba(0, 0, 0, .25))), -webkit-gradient(linear, left top, right bottom, color-stop(0, rgba(255, 255, 255, 0)), color-stop(0.5, rgba(255, 255, 255, .1)), color-stop(0.501, rgba(255, 255, 255, 0)), color-stop(1, rgba(255, 255, 255, 0)));
  background: -moz-linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), -moz-linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  background: linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  opacity: 0;
  transition: opacity 0.2s linear;
}

.logoBox.active::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, .15)), to(rgba(0, 0, 0, .25))), -webkit-gradient(linear, left top, right bottom, color-stop(0, rgba(255, 255, 255, 0)), color-stop(0.5, rgba(255, 255, 255, .1)), color-stop(0.501, rgba(255, 255, 255, 0)), color-stop(1, rgba(255, 255, 255, 0)));
  background: -moz-linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), -moz-linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  background: linear-gradient(top, rgba(255, 255, 255, .15), rgba(0, 0, 0, .25)), linear-gradient(left top, rgba(255, 255, 255, 0), rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0));
  opacity: 1;
  transition: opacity 0.2s linear;
}

.logoBox.active {
  transform: perspective(600px) rotateY(35deg);
  box-shadow: -3px -1px 6px rgba(0, 0, 0, .7);
  transition: all 0.2s linear;
}

.logoBox img {
  object-fit: scale-down;
  height: 100%;
  width: 100%;
}

.storeList {
  display: flex;
  flex-direction: column;
  position: absolute;
  visibility: hidden;
  opacity: 0;
  margin: 0;
  width: 24vw;
  height: auto;
  border: 1px solid rgb(40, 40, 40);
  padding: 0.2vw;
  border-radius: 5px;
  background-color: rgb(10, 10, 10, 0.9);
  box-shadow: -2px -1px 4px rgba(0, 0, 0, .7);
  transition: all 0.2s linear;
}

.storeList ul,
a {
  text-decoration: none;
  list-style: none;
}

.storeList.active {
  position: absolute;
  visibility: visible;
  opacity: 1;
}

.chryslerLocations {
  bottom: 5vw;
  left: 1vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, visibility 0.2s linear;
}

.chryslerLocations.active {
  bottom: 10vw;
  left: 1vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, visibility 0.2s linear;
}

.dodgeLocations {
  bottom: 5vw;
  left: 10vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, z-index 0.2s linear;
}

.dodgeLocations.active {
  bottom: 10vw;
  left: 10vw;
  transition: opacity 0.2s linear, bottom 0.2s linear, z-index 0.2s linear;
}

.jeepLocations {
  position: absolute;
  opacity: 0;
  bottom: 5vw;
  left: 19vw;
  transition: opacity 0.2s linear, bottom 0.2s linear;
}

.jeepLocations.active {
  bottom: 10vw;
  left: 19vw;
  transition: opacity 0.2s linear, bottom 0.2s linear;
}

.location {
  display: flex;
  flex-direction: row-reverse;
  flex-wrap: wrap;
  width: 100%;
  height: 25%;
  padding: 0.2vw;
  font-family: "DDC Font Face", "Open Sans", Arial, Helvetica, sans-serif;
  font-weight: bold;
  font-size: 1vw;
  letter-spacing: 0.03vw;
  border-radius: 5px;
  background-color: rgb(255, 8, 0, 0);
  transition: all 0.15s linear;
  color: #ff0800;
  align-items: center;
  justify-content: flex-end;
}

.location:hover {
  color: white;
  background-color: rgb(255, 8, 0, 0.7);
  transition: all 0.15s linear;
}

.locationName {
  width: 75%;
}

.location img {
  object-fit: scale-down;
  align-self: flex-start;
  width: 20%;
  height: 100%;
  margin: 2% 2% 0 0;
}

.locationSubInfo {
  display: flex;
  flex-direction: column;
  font-family: "DDC Font Face", "Open Sans", Arial, Helvetica, sans-serif;
  color: white;
  font-weight: normal;
  font-size: 0.7vw;
  margin-left: 22%;
}

.flexBreak {
  flex-basis: 100%;
  width: 0;
}
<body style="background-color: darkgrey;">
  <div >
    <nav >
      <!-- chrysler -->
      <div id="chryslerContainer" >
        <div id="chryslerLogoBox" >
          <img alt="Chrysler Logo" src="https://i.imgur.com/m33KwSY.png">
        </div>
        <div id="chryslerLocations" >
          <ul>
            <li id="chryslerLocation1">
              <a href="" >
                <span >Location Name</span>
                <img alt="Chrysler Logo" src="https://i.imgur.com/m33KwSY.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <!-- dodge -->
      <div id="dodgeContainer" >
        <div id="dodgeLogoBox" >
          <img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
        </div>
        <div id="dodgeLocations" >
          <ul>
            <li id="dodgeLocation1">
              <a href="" >
                <span >Location Name</span>
                <img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
            <li id="dodgeLocation2">
              <a href="" >
                <span >Location Name</span>
                <img alt="Dodge Logo" src="https://i.imgur.com/ZFAs0ed.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <!-- jeep -->
      <div id="jeepContainer" >
        <div id="jeepLogoBox" >
          <img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
        </div>
        <div id="jeepLocations" >
          <ul>
            <li id="jeepLocation1">
              <a href="" >
                <span >Location Name</span>
                <img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
            <li id="jeepLocation2">
              <a href="" >
                <span >Location Name</span>
                <img alt="Jeep Logo" src="https://i.imgur.com/BmMaVKr.png">
                <span >
                  <b>Address</b>
                  <div ></div>
                  Phone1<div ></div>
                  Phone2<div ></div>
                  Phone3<div ></div>
                </span>
              </a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  </div>
</body>

  • Related