0

In a previous question a made here, I tried to know if there was a way to change the height of my pictures when I click on them, and then when I click them again they return to the original height with vanilla JS. A fellow user (thank you Butalin) provided me with a workable function but with a caveat: - If I click on the same picture, the behaviour is as expected. One click changes height, the next click changes back to inital height. - If i click on one picture to change height, and don't click again on the same picture but instead, click on the other, it changes the height of the next one (as expected), but does not change the height of the first one to normal. Is it possible to do this?

Here's my code:

var element = document.querySelectorAll('.imgclasstosize');

element.forEach(el => {
  el.addEventListener('click', function(event) {
    if (event.target.style.height == '500px') {
      event.target.style.height = '80px';
    } else {
      event.target.style.height = '500px';
    }
  });
});

and here is the HTML:

<img class="imgclasstosize" src="https://assets.picspree.com/variants/FRgeQ4d3pFXszVF7QW9VBgFQ/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d">

<img class="imgclasstosize" src="https://assets.picspree.com/variants/RXCuAnyzqoapjkZQuhDFwBMs/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d">```
jm3
  • 11
  • 4
  • 2
    Checking CSS properties directly is [best avoided](/q/55071684/4642212). Instead, a CSS class should be used, e.g. `.imgclasstosize{ height: 80px; } .imgclasstosize.expanded { height: 500px; }`; then [`.classList.has("expanded")`](//developer.mozilla.org/en-US/docs/Web/API/Element/classList) to check for its existence, `.classList.toggle("expanded")` for toggling, etc. – Sebastian Simon Jun 03 '21 at 13:19
  • Use [event delegation](//developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_delegation) instead of assigning multiple event listeners — it’s more maintainable, and applies to dynamically added elements. E.g., use an [event argument](//developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#The_event_listener_callback)’s [`target`](//developer.mozilla.org/en-US/docs/Web/API/Event/target). See [the tag info](/tags/event-delegation/info) and [What is DOM Event delegation?](/q/1687296/4642212). – Sebastian Simon Jun 03 '21 at 13:20
  • 1
    Using a CSS class would also help by letting you do `element.forEach(el => el.classList.remove("expanded"))` to set all of the elements back to normal size, then add it back to the one that was clicked. No need to check for existence or anything else. – Heretic Monkey Jun 03 '21 at 13:24
  • I added said classes in the CSS and checked the classlist for expanded in the if-loop, but now nothing happens. this is what i have. ` element.forEach(el => { el.addEventListener('click', function() { if (element.classList.has("expanded")) { element.classList.toggle("expanded"); } else { } }); }); ` – jm3 Jun 03 '21 at 13:54

2 Answers2

1

The behavior described by OP is called mutual exclusion. The procedure called Event Delegation is used in the demo below to facilitate mutual exclusion.

Details are commented in demo below

// Reference parent element
const gallery = document.querySelector('.gallery');
// Collect all .img into a NodeList
const images = document.querySelectorAll('.img');
// Register parent element as click listener
gallery.onclick = toggleHeight;

// Event handler passes Event Object
function toggleHeight(event) {
  /* .currentTarget
  Event property determines which
  element listens for the click event
  ie .gallery the parent
  */
  const listener = event.currentTarget;
  /* .target
  Event property determines which 
  element the user actually clicked 
  directly
  ie ANY element within .gallery
  */
  const clicked = event.target;

  /* Event Delegation
  By using Event properties and flow
  control statements like "if", we can
  make specific elements behave when the
  event happens and exclude all other
  elements
  */
  /* Delegating...
  1. ensure that the clicked element is
     within .gallery...
  2. then ensure that the clicked element
     has the class: ".img"
  If the user clicked an element that 
  does not fulfill those two requirements, 
  nothing will happen.
  */
  if (clicked !== listener) {
    if (clicked.classList.contains('img')) {
      /* Mutual Exclusion
      A behavior shared by a group of
      objects wherein the desired 
      effect can only occur on a single
      object at a time.
      */
      /*
      for each ".img" check to see if it's
      clicked and toggle the class ".tall"
      if it wasn't clicked then remove the
      class ".tall" from it.
      */
      images.forEach(img => {
        if (img === clicked) {
          img.classList.toggle('tall');
        } else {
          img.classList.remove('tall');
        }
      });
    }
  }
  /* 
  Stop the click event from going on to
  the ancestor elements (ie stop Event
  Bubbling)
  */
  event.stopPropagation();
};
.gallery {
  width: 90vw;
  height: auto;
  margin: 8px auto;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}

.img,
.small {
  max-height: 80px;
  max-width: 20vw;
  margin: 4px auto;
  display: block;
}

.img {
  cursor: pointer;
}

.tall {
  min-height: 500px;
}

figure {
  width: max-content;
  margin: auto 30vw;
}
<section class='gallery'>
  <img class="img" src="https://assets.picspree.com/variants/FRgeQ4d3pFXszVF7QW9VBgFQ/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d">

  <img class="img" src="https://assets.picspree.com/variants/RXCuAnyzqoapjkZQuhDFwBMs/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d">

  <img class="img" src="https://assets.picspree.com/variants/Q6FeBijP3z1pVPPu9ZrDsebE/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d">

  <figure>
    <img class='small' src='https://assets.picspree.com/variants/4yZoj6m5FAQkh5vgJbrkrrMC/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d'>
    <figcaption>I'll never be tall because I don't have class ".img"</figcaption>
  </figure>
</section>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Thank you for your answer. I ended up trying the solution of user code_monk first and that did the trick. I believe yours would solve my problem to, but requires a bit more reading of all the details which is important. I assure you I will try to execute it later as the concept of Event Delegation and Mutual Exclusion are of utmost importance to my growing and learning. Thank you again. – jm3 Jun 03 '21 at 17:20
  • 1
    Event delegation is so clean to manage a lot of click events, it reduce dramatically the number of listeners, great, in the case of the OP, that he have 2 options it doesn't matter, it's not a big deal, Great answer (y) – butalin Jun 03 '21 at 18:35
0

Yes, this is very possible. You can use Array.from to operate on a DOM list as if it was an array. You can use classList.toggle to toggle CSS classes, and then put your dimensions in the CSS classes:

const allImages = document.querySelectorAll('.imgclasstosize');

allImages.forEach(el => {
  el.addEventListener('click', function(event) {
  
    const thisImg = event.target;
    const allOtherImages = Array.from(allImages).filter(img => {
       return img !== thisImg;
    });
  
    allOtherImages.forEach(img => {
        img.classList.remove('big')
    });
  
    thisImg.classList.toggle('big');
  
  });
});
img.imgclasstosize {
    height: 80px;
    border: 1px solid gray;
    transition: height 1s ease;
}

img.imgclasstosize.big {
    height: 250px;
}
<img class="imgclasstosize little" src="https://assets.picspree.com/variants/FRgeQ4d3pFXszVF7QW9VBgFQ/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d" />

<img class="imgclasstosize little" src="https://assets.picspree.com/variants/RXCuAnyzqoapjkZQuhDFwBMs/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d" />
code_monk
  • 9,451
  • 2
  • 42
  • 41
  • Thank you very much for your answer. This did precisely what I set out to do. Again thank you. – jm3 Jun 03 '21 at 17:16
  • Is there any way for this transition from be smooth? – jm3 Jun 23 '21 at 14:14
  • Yes, you can add a CSS transition. Run my code again, and see the line `transition: height 1s ease;` in the CSS section – code_monk Jun 23 '21 at 20:48