javascript

How can I flip DIVs on click, with interaction between each other (one flips open, the other closes)?


I'm new to web development and I challenged myself with this task to master JavaScript. My task is to make different divs on the page flippable on click and interact with each other based on the current status.

I would like to learn two more things:

  1. shorten/simplify the current JS code (if possible)
  2. Have the divs interact with each other: when 'on click' the current one opens, but if any other of them was already open, it'll close. So only one div stay open at a time.

HTML

var card = document.querySelector('.card');
card.addEventListener( 'click', function() {
  card.classList.toggle('is-flipped');
});

var card2 = document.querySelector('.card2');
card2.addEventListener( 'click', function() {
  card2.classList.toggle('is-flipped');
});

var card3 = document.querySelector('.card3');
card3.addEventListener( 'click', function() {
  card3.classList.toggle('is-flipped');
});
@media screen and (min-width:650px){
  .flipping {display: flex;
      justify-content:space-between;
  }
}

.scene, .scene2, .scene3 {
  width: 200px;
  height: 200px;
  perspective: 1200px;
  margin-left: auto;
  margin-right: auto;
}

.card, .card2, .card3 {
  width: 200px;
  height: 200px;
  position: relative;
  transition: transform 1.5s;
  transform-style: preserve-3d;
}

.card__face, .card__face2, .card__face3 {
  position: absolute;
  width: 200px;
  height: 200px;
  backface-visibility: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}

.card__face--front, .card__face--front2, .card__face--front3 {
  background: red;
}

.card__face--back, .card__face--back2, .card__face--back3 {
  background: blue;
  transform: rotateY( 180deg );
}

.card.is-flipped, .card2.is-flipped, .card3.is-flipped {
  transform: rotateY(180deg);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script defer src="/script.js"></script>
    <link rel="stylesheet" href="style.css">

    <title>Document</title>
</head>
<body>
    <section class="flipping">
        <div class="scene">
          <div class="card">
            <div class="card__face card__face--front">Front of first div</div>
            <div class="card__face card__face--back">Back of first div</div>
          </div>
        </div>
        <br>
        
        <div class="scene2">
          <div class="card2">
            <div class="card__face2 card__face--front2">Front of second div</div>
            <div class="card__face2 card__face--back2">Back of second div</div>
          </div>
        </div>        
        <br>
        
        <div class="scene3">
          <div class="card3">
            <div class="card__face3 card__face--front3">Front of last div</div>
            <div class="card__face3 card__face--back3">Back of last div</div>
          </div>
        </div>          
    </section
    
</body>
</html>

Would you be able to help me out?

Thanks!


Solution

  • You could simplify your code by using a common card class on all your cards. Then, you would need to remove the is-flipped class from all cards before flipping the clicked one:

    var cards = document.querySelectorAll('.card');
    
    cards.forEach(function (card) {
      card.addEventListener('click', function () {
         // Remove the class on all, toggle the clicked one
         cards.forEach(function (c) {
           if (c !== card) c.classList.remove('is-flipped');
           else c.classList.toggle('is-flipped');
         });
      });
    });
    

    Fixed CodePen

    Stack Snippet:

    var cards = document.querySelectorAll('.card');
    
    cards.forEach(function (card) {
      card.addEventListener('click', function () {
         // Remove the class on all, except the clicked one
         cards.forEach(function (c) {
           if (c !== card) c.classList.remove('is-flipped');
           else c.classList.toggle('is-flipped');
         });
      });
    });
    @media screen and (min-width:400px){
      .flipping {display: flex;
          justify-content:space-between;
      }
    }
    
    .scene, .scene2, .scene3 {
      width: 100px;
      height: 100px;
      perspective: 1200px;
      margin-left: auto;
      margin-right: auto;
    }
    
    .card {
      width: 100px;
      height: 100px;
      position: relative;
      transition: transform 1.5s;
      transform-style: preserve-3d;
    }
    
    .card__face, .card__face2, .card__face3 {
      position: absolute;
      width: 100px;
      height: 100px;
      backface-visibility: hidden;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
    }
    
    .card__face--front, .card__face--front2, .card__face--front3 {
      background: red;
    }
    
    .card__face--back, .card__face--back2, .card__face--back3 {
      background: blue;
      transform: rotateY( 180deg );
    }
    
    .card.is-flipped, .card2.is-flipped, .card3.is-flipped {
      transform: rotateY(180deg);
    }
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <script defer src="/script.js"></script>
        <link rel="stylesheet" href="style.css">
    
        <title>Document</title>
    </head>
    <body>
        <section class="flipping">
            <div class="scene">
              <div class="card">
                <div class="card__face card__face--front">Front of first div</div>
                <div class="card__face card__face--back">Back of first div</div>
              </div>
            </div>
            <br>
            
            <div class="scene2">
              <div class="card">
                <div class="card__face2 card__face--front2">Front of second div</div>
                <div class="card__face2 card__face--back2">Back of second div</div>
              </div>
            </div>        
            <br>
            
            <div class="scene3">
              <div class="card">
                <div class="card__face3 card__face--front3">Front of last div</div>
                <div class="card__face3 card__face--back3">Back of last div</div>
              </div>
            </div>          
        </section
        
    </body>
    </html>