0

I'm not fully understanding 'this', I have checked another answer to the same question, but as the base code was quite different I found I was getting lost. I couldn't figure out what I should be binding meanwhile some of the other answers didn't use 'this' or binding at all.

I am trying to do something very simple, add and remove a class from a single element when clicked, but I want to apply this to multiple elements with only the one clicked triggering each time.

I would like to understand and do this in JS, not jQuery. Which would be an easy short cut but leave me just as baffled.

const flipCard = document.querySelectorAll(".card--holder");
const card = document.getElementById("card");

flipCard.forEach(card => {
  card.addEventListener("click", flipCards);
})

const flipCards = () => {
  if (card.classList.contains("flipped")) {
    this.classList.remove("flipped");
  } else {
    this.classList.add("flipped");
  }
}
.flip {
  -webkit-perspective: 800;
  width: 400px;
  height: 200px;
  position: relative;
  margin: 50px auto;
}
.flip .card.flipped {
  -webkit-transform: rotateY(-180deg);
}
.flip .card {
  width: 100%;
  height: 100%;
  -webkit-transform-style: preserve-3d;
  -webkit-transition: 0.5s;
}
.flip .card .face {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-backface-visibility: hidden;
  z-index: 2;
  text-align: center;
  line-height: 200px;
}
.flip .card .front {
  position: absolute;
  z-index: 1;
  background: black;
  color: white;
  cursor: pointer;
}

.flip .card .back {
  -webkit-transform: rotateY(180deg);
  background: blue;
  color: black;
  cursor: pointer;
}
<div class="card--holder flip">
    <div class="card" id="card">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>


<div class="card--holder flip">
    <div class="card" id="card">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>

<div class="card--holder flip">
    <div class="card" id="card">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>
GoldenGonaz
  • 1,116
  • 2
  • 17
  • 42
  • you should use unique identifiers for id attribute or get cards by query selector as well with class. – Max Sinev May 23 '18 at 08:47
  • 2
    @MaxSinev thats not the answer for what he asked, there is definitely a solution. – Akhil Aravind May 23 '18 at 08:50
  • The problem is that `flipCards` is an arrow function, which closes over the `this` where it's defined rather than having it set by how it's called. See the linked question's answers for details. The solutions are either 1. Don't use an arrow function so that `this` is controlled by how the function is called (and thus will refer to the element the event was hooked on), or 2. Keep using an arrow and accept the event object you receive as a parameter, and use its `currentTarget` property. That will be what `this` would be with a traditional function. – T.J. Crowder May 23 '18 at 08:50
  • Note: `id`s **must** be unique in a document, you can't have the same `id` on more than one element. (But it's a side issue to what you're asking.) – T.J. Crowder May 23 '18 at 08:51
  • Also in this case you can't use `flipCards` before it's defined because it's defined using `const`. see [this](https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6). – Amr Noman May 23 '18 at 08:58

1 Answers1

0

Some points:

  • You need to define the flipCards constant before you use it.

  • ids have to be unique in HTML

  • this is incorrect in arrow functions, you need to use the first function parameter and target property to get the click target element. And as you want to have the parent selected, you need to get this with closest, because the click target is inside of the element, in your example.

const flipCards = event => {
  var parent = event.target.closest(".card--holder");

  if (parent.classList.contains("flipped")) {
    parent.classList.remove("flipped");
  } else {
    parent.classList.add("flipped");
  }
}

const flipCard = document.querySelectorAll(".card--holder");
const card = document.getElementById("card");

flipCard.forEach(card => {
  card.addEventListener("click", flipCards);
})
.flip {
  -webkit-perspective: 800;
  width: 400px;
  height: 200px;
  position: relative;
  margin: 50px auto;
}
.flip .card.flipped {
  -webkit-transform: rotateY(-180deg);
}
.flip .card {
  width: 100%;
  height: 100%;
  -webkit-transform-style: preserve-3d;
  -webkit-transition: 0.5s;
}
.flip .card .face {
  width: 100%;
  height: 100%;
  position: absolute;
  -webkit-backface-visibility: hidden;
  z-index: 2;
  text-align: center;
  line-height: 200px;
}
.flip .card .front {
  position: absolute;
  z-index: 1;
  background: black;
  color: white;
  cursor: pointer;
}

.flip .card .back {
  -webkit-transform: rotateY(180deg);
  background: blue;
  color: black;
  cursor: pointer;
}

.flipped .card .front {
  background: red !important;
}
<div class="card--holder flip">
    <div class="card" id="card1">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>

<div class="card--holder flip">
    <div class="card" id="card2">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>

<div class="card--holder flip">
    <div class="card" id="card3">
        <div class="face front">Front</div>
        <div class="face back">Back</div>
    </div>
</div>
eisbehr
  • 12,243
  • 7
  • 38
  • 63