How to get value of closest hidden input field on button click

Viewed 372

I have this code in a loop ie: each delete button and csrf token shows as many times as there are database records:

<% for (const products of approved) { %>
<li>
    <div class="list-box-listing">
        <div class="list-box-listing-img"><a href="#"><img src="../<%= products.image %>" alt=""></a></div>
        <div class="list-box-listing-content">
            <div class="inner">
                <h3><a href="#"><%= products.title %></a></h3>
                <span><%= products.address.city %>, <%= products.address.suburb %></span>
                <div class="star-rating">
                    <h5>Added <%= moment(products.createdAt).fromNow() %></h5>
                </div>
            </div>
        </div>
    </div>
    <div class="buttons-to-right">
        <a href="edit-listing/<%= products._id %>" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>
        <input type="hidden" name="_csrf" value="<%= csrfToken %>">
        <a href="#" class="button gray delete" data-id="<%= products._id %>"><i class="sl sl-icon-close"></i> Delete</a>
    </div>
</li>
<% } %>

I want to get the value of the csrf token that is in the same vicinity as the button that was clicked.

document.body.addEventListener("click", e => {
  if (!e.target.matches(".delete")) return;
  e.preventDefault();


  const deleteBtn = e.target;
  const csrf = document.querySelector("[name=_csrf").value;
  const productId = deleteBtn.dataset.id;

  console.log(csrf);

});

I tried using 'closest' but that was an epic fail.

  const deleteBtn = e.target;
  const csrf = deleteBtn.closest("[name=_csrf]").value;

I also tried:

const csrf = deleteBtn.parentNode.querySelector("[name=_csrf]").value;

But that gives me error:

Uncaught TypeError: Cannot read property 'value' of null at HTMLBodyElement.document.body.addEventListener.e

3 Answers

In the demos the e.currentTarget has changed from:

document.body.addEventListener(...

to:

document.querySelector('.buttons-to-right').addEventListener(...

Use previousElementSibling, see Demo 1. If that doesn't work, try using e.currentTarget and then get a reference from there, see Demo 2. BTW you can apply the following to <a>nchors that are used for buttons:

<a href="#/" ...

The forward slash / disables the default behavior of jumping so you don't have to use e.preventDefault().


Demo 1

const getCSRF = e => {

  const tgt = e.target;
  if (tgt.matches(".delete")) {

    const input = tgt.previousElementSibling;
    const csrf = input.value;
    const productId = tgt.dataset.id;

    console.log(csrf);
  }
};

document.querySelector('.buttons-to-right').addEventListener("click", getCSRF);
<li>
  <div class="list-box-listing">
    <div class="list-box-listing-img"><a href="#"><img src="../<%= products.image %>" alt=""></a></div>
    <div class="list-box-listing-content">
      <div class="inner">
        <h3>
          <a href="#"></a>
        </h3>
        <span></span>
        <div class="star-rating">
          <h5>Added </h5>
        </div>
      </div>
    </div>
  </div>
  <div class="buttons-to-right">
    <a href="edit-listing/<%= products._id %>" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>

    <input type="hidden" name="_csrf" value="<%= csrfToken %>">
    <a href="#/" class="button gray delete" data-id="<%= products._id %>"><i class="sl sl-icon-close"></i> Delete</a>
  </div>
</li>


Demo 2

const getCSRF = e => {

  const tgt = e.target;
  const cur = e.currentTarget;

  if (tgt.matches(".delete")) {

    const input = cur.querySelector('[name=_csrf]');
    const csrf = input.value;
    const productId = tgt.dataset.id;

    console.log(csrf);
  }

};

document.querySelector('.buttons-to-right').addEventListener("click", getCSRF);
<li>
  <div class="list-box-listing">
    <div class="list-box-listing-img"><a href="#"><img src="../<%= products.image %>" alt=""></a></div>
    <div class="list-box-listing-content">
      <div class="inner">
        <h3>
          <a href="#"></a>
        </h3>
        <span></span>
        <div class="star-rating">
          <h5>Added </h5>
        </div>
      </div>
    </div>
  </div>
  <div class="buttons-to-right">
    <a href="edit-listing/<%= products._id %>" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>

    <input type="hidden" name="_csrf" value="<%= csrfToken %>">

    <a href="#/" class="button gray delete" data-id="<%= products._id %>"><i class="sl sl-icon-close"></i> Delete</a>
  </div>
</li>

Try getting the containing element and query for the target field, like so:

document.querySelectorAll('a.delete').forEach(button => {
  button.addEventListener('click', function(e) {
    e.preventDefault();

    var csrf = this.parentElement.querySelector('[name="_csrf"]').value;

    console.log(csrf);
  });
});
<li>
  <div class="list-box-listing">
    <div class="list-box-listing-img">
      <a href="#">
        <img src="https://via.placeholder.com/200" alt="product-image">
      </a>
    </div>
    <div class="list-box-listing-content">
      <div class="inner">
        <h3><a href="#">Product title 1</a></h3>
        <span>Address 2</span>
        <div class="star-rating">
          <h5>Added 01/15/2019</h5>
        </div>
      </div>
    </div>
  </div>
  <div class="buttons-to-right">
    <a href="edit-listing/1" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>
    <input type="hidden" name="_csrf" value="TOKEN_VALUE_1">
    <a href="#" class="button gray delete" data-id="123"><i class="sl sl-icon-close"></i> Delete</a>
  </div>
</li>

<li>
  <div class="list-box-listing">
    <div class="list-box-listing-img">
      <a href="#">
        <img src="https://via.placeholder.com/200" alt="product-image">
      </a>
    </div>
    <div class="list-box-listing-content">
      <div class="inner">
        <h3><a href="#">Product title 2</a></h3>
        <span>Address 2</span>
        <div class="star-rating">
          <h5>Added 01/15/2019</h5>
        </div>
      </div>
    </div>
  </div>
  <div class="buttons-to-right">
    <a href="edit-listing/1" class="button gray edit"><i class="sl sl-icon-note"></i> Edit</a>
    <input type="hidden" name="_csrf" value="TOKEN_VALUE_2">
    <a href="#" class="button gray delete" data-id="456"><i class="sl sl-icon-close"></i> Delete</a>
  </div>
</li>

Use this script.

    <script>
    document.body.addEventListener("click", e => {
        if (!e.target.matches(".delete")) return;
        e.preventDefault();
        const deleteBtn = e.target;
        const closestDiv = deleteBtn.closest("[class=buttons-to-right]");
        const csrf = closestDiv.querySelector("[name=_csrf]").value;
        console.log(csrf);
    });
</script>
Related