-2

im making item cards with ojct from sql and with foreach i create cards. ive made when i press a card it selects itself with a red border and the buy button changes to edit ant delete. but it only works with the first card the other cards dont work and i cant find reason.

<?php $i = 0;  ?>
<div class="container marketing">

  <?php foreach ($devilRooms as $devilRoom) { ?>
  <?php $i++;  ?>
  <div class="card optionsecoptions" id="card" style="width: 18rem;">
    <?php
      echo '<img class="bd-placeholder-img bd-placeholder-img-lg feature-image img-fluid mx-auto" width="500" height="500" src="img/nr' . $i . '.png" alt="">';
      ?>
      <div class="card-body">
        <h5 class="card-title">
          <?= $devilRoom->brand ?>
        </h5>
        <h5 class="card-title">
          <?= $devilRoom->item ?>
        </h5>
        <hr>
        <p>
          <?= $devilRoom->descript ?>
        </p>
        <hr>
        <p class="price">
          <?= $devilRoom->price ?>$</p>
      </div>
      <div class="card-body">
        <form class="button" id="edit" action="" method="post">
          <input type="hidden" name="id" value="<?= $devilRoom->id ?>">
          <button type="submit" name="edit" class="btn btn-primary">edit</button>
        </form>
        <br>
        <form class="button" id="destroy" action="" method="post">
          <input type="hidden" name="id" value="<?= $devilRoom->id ?>">
          <button type="submit" name="destroy" class="btn btn-danger">delete</button>
        </form>
        <form class="button" id="buy" action="" method="post">
          <input type="hidden" name="id" value="<?= $devilRoom->id ?>">
          <button type="submit" name="buy" class="btn btn-success">buy</button>
        </form>
      </div>
  </div>
  <?php } ?>
</div>

<script>
  document.body.addEventListener('click', function(event) {
    var closestOption = findClosestByClassName(event.target, 'optionsecoptions');
    if (closestOption) {
      var selectedEl = document.querySelector(".selected");

      if (selectedEl && selectedEl !== closestOption) {
        selectedEl.classList.remove("selected");
      }

      closestOption.classList.toggle("selected");
    }
  });


  const btn = document.getElementById('card');

  btn.addEventListener('click', () => {
    const edit = document.getElementById('edit');
    const destroy = document.getElementById('destroy');
    const buy = document.getElementById('buy');

    if (edit.style.display === 'none' && destroy.style.display === 'none' && buy.style.display === 'block') {

      edit.style.display = 'block';
      destroy.style.display = 'block';
      buy.style.display = 'none';
      
    } else {
      edit.style.display = 'none';
      destroy.style.display = 'none';
      buy.style.display = 'block';
    }
  });


  function findClosestByClassName(el, cls) {
    while (!el.classList.contains(cls)) {
      el = el.parentNode;
      if (!el) {
        return null;
      }
    }
    return el;
  }
</script>

ive added a video how it works

all of them should work the same as the first one

1 Answers1

0

Near the start in your <?php foreach loop you have

<div class="card optionsecoptions" id="card" style="width: 18rem;">

Using that id="card" means you are repeating this id for every card. The id must be unique on the page. Your document.getElementById('card'); gets the first one only, so that first card is the only one that gets an event handler and "only hides and shows first card buttons".

You will have a similar problem with <form class="button" id="edit" ...> and anywhere you use id within the loop.

One way to solve this is to eliminate all these ids and use classes instead, and then in place of document.getElementById you would probably use document.querySelectorAll('.some.css.selector')

I'm going to leave out a lot, but your current basic structure minus the use of id is

<div class="card card-trigger"> <!-- I'm adding class "card-trigger" -->
    <div class="card-body">
       <!-- some <h5>s and <p>s -->
    </div><!-- card-body -->

    <div class="card-body">
        <button type="submit" name="edit" class="btn btn-primary">edit</button>
        <button type="submit" name="delete" class="btn btn-primary">delete</button>
        <button type="submit" name="buy" class="btn btn-primary">buy</button>
    </div><!-- card-body -->
</div>

Now...

const btn = document.getElementById('card');

btn.addEventListener('click', () => { .......

becomes...

const allButtons = document.querySelectorAll('.card-trigger');

allButtons.forEach(b => b.addEventListener('click', (e) => {
    // added param 'e' for the event, you can use e.target to find what was clicked
});

This allButtons.forEach(...) adds a click event handler to each of the cards.

The rest of the event handler largely stays the same, except again you can't use the id to get your things like const edit = document.getElementById('edit'); but you can use querySelector on the card to find the buttons within that particular card (it doesn't have to be "document.getElementById" or "document.querySelectorAll" — you can use "somediv.querySelectorAll" to find things only within that somediv)
and the card that was clicked is "e.target" so you could have e.target.querySelector('.buy-btn') to find the "buy" button that is in the card that was clicked.

If you have a very large number of cards adding a handler to every one of them can be resource intensive, and you might want to learn about event delegation instead of using this approach.

Stephen P
  • 14,422
  • 2
  • 43
  • 67
  • sorry probably dumb question but how do i use e.target ? – danielkalinin Sep 26 '22 at 21:58
  • @danielkalinin - if you attach an event listener to anything, that listener receives an "event" parameter when it is called ... but to access it you have to declare your function with the parameter, which I usually just call `e` — so `someDiv.addEventListener('click', function(e) { ... if (e.target == something) ... e.target.display = none; ... etc });` – Stephen P Sep 27 '22 at 01:07
  • @danielkalinin Here's an example, let me know if you can't get to it. I'm eventually going to delete this comment. --> https://replit.com/@stephen61/ForDanielKalinin#index.js – Stephen P Sep 27 '22 at 01:23