Remove click event on specific element where the event is called in multiple places

189 views Asked by At

I have multiple card generated from a loop which share the same functionality. One of them is to Like and Dislike buttons. It's someone's code, I was asked to remove the click event per card once the user click on Like/Dislike button. I don't know how to remove/disable the click event when one choice is picked as it might disable other cards buttons.

I updated the original code for more clarity

function contentLike_Dislike(contentid, islike) {
  if (islike === "true") {
    var likeP = $("#p-like-" + contentid).text();
    $("#p-like-" + contentid).text(parseInt(likeP) + 1);

    $(".fa-thumbs-up").click(function () {
      $(this).removeClass("far").addClass("fa"); 
    });
  } else {
    var dislikeP = $("#p-dislike-" + contentid).text();
    $("#p-dislike-" + contentid).text(parseInt(dislikeP) + 1);

    $(".fa-thumbs-down").click(function () {
      $(this).removeClass("far").addClass("fa"); 
    });
  }
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>

<div class="col-lg-6 d-flex justify-content-lg-end">
  <a href="javascript:void(0)"
     onclick="contentLike_Dislike('21785', 'true')"
     class="text-navy-custom font-r fs-small me-3 d-flex"
  >
    <i class="far align-self-center fa-thumbs-up me-2 align-self-center"
       aria-hidden="true">
    </i>
    <p id="p-like-21785"
       class="mb-0 align-self-center d-none"
       tabindex="0">
      0
    </p>
  </a>
  <a href="javascript:void(0)"
     onclick="contentLike_Dislike('21786', 'false')"
     class="text-navy-custom font-r fs-small me-3 d-flex"
  >
    <i class="far align-self-center fa-thumbs-down me-2 align-self-center"
       aria-hidden="true">
    </i>
    <p id="p-dislike-21786"
       class="mb-0 align-self-center d-none"
       tabindex="0"
    >
      0
    </p>
  </a>
</div>

3

There are 3 answers

0
Eolo On BEST ANSWER

By using the parent element to select buttons, you can disable only those that are within the same parent. This is optimal for the situation where you have multiple cards on the same page.

function contentLike_Dislike(event, islike) {
    if (islike === 'true') {
      alert("like");
    } else {
      alert("dislike");
    }
    
    var buttons = event.target.parentNode.querySelectorAll("button");
    buttons.forEach((item) => {
        item.onclick = "";
    });
}
<div class="card">
  <button onclick="contentLike_Dislike(event, 'true')">Like</button>
  <button onclick="contentLike_Dislike(event, 'false')">Dislike</button>
</div>

2
imvain2 On

Normally I would recommend an overhaul that involved using an event handler instead of inline click handler, but it seems like there is a bunch of these buttons on the same page that will need to be fixed.

So here is a simple solution, have a variable outside of the function that is initially set to false. Then in your function, check to see if its false and at the end set it to true. I have console.logs just to show what is happening when clicked.

UPDATE: Now that the OP has updated their question and comments with more information, I have updated my answer by storing the IDs in an array.

let liked = []

function contentLike_Dislike(id,islike) {
  const hasLiked = liked.includes(id)
  if (islike === "true" && !hasLiked) console.log("like")
  else if (islike === "false" && !hasLiked) console.log("dislike")
  if(hasLiked)console.log("already liked")
  liked.push(id);
}
<button onclick="contentLike_Dislike(1,'true')">Like</button>
<button onclick="contentLike_Dislike(1,'false')">Dislike</button>

7
Peter Seliger On

I propose an approach based on event delegation.

The advantage comes not only with getting rid of the OP's inline scripting, but also with the registering and handling the event at exactly a single root-element for each voting-set (or card-set) where each root-element does enclose the elements which actually do trigger the to be handled event. Thus, it also allows code-reuse for more than just one voting- or card-set.

A clean approach would allow a handler-function to retrieve all necessary information from the elements that are related to an event. One way of achieving it is the usage of both, some data-* global attributes and its HTMLElement counterpart, the dataset property.

Within the handler-function one also would remove this very handler itself from the element it has been before registered to.

The markup of the next provided implementation resembles the OP's latest changes on the OP's provided HTML-code in structure and functionality. Of cause class-names can be added again as needed; there is just no reason for introducing any kind of id attributes. The markup already before was structured enough in order to not rely on any ids.

function handleVote({ target, currentTarget }) {
  // - always assure the intended target especially in case
  //   of such a target does contain (nested) child elements.
  target = target.closest('[data-vote-value]');

  if (target) {
    // - in case of a valid, intended target remove the event
    //   handler immediately from the delegate/current target.
    currentTarget.removeEventListener('click', handleVote);

    const elmCount = target.querySelector('[data-vote-count]');
    const elmIcon = target.querySelector('.icon');

    // - read, sanitize and increment the count specific
    //   `dataset` value and update it wherever necessary.
    const count = parseInt(elmCount.dataset.voteCount ?? 0, 10) + 1;

    elmCount.dataset.voteCount = count;
    elmCount.textContent = count;

    // - update the icon specific class names accordingly.
    elmIcon.classList.add('fa');
    elmIcon.classList.remove('far');
  }
}

document
  .querySelectorAll('[data-voting]')
  .forEach(rootNode =>
    rootNode.addEventListener('click', handleVote)
  );
[data-voting] { margin: 10px; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>

<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>
<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>
<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>

A comparison of the above pure DOM-Api implementation with the below jQuery-based counterpart, hopefully leads to ones conclusion that jQuery is obsolete for just DOM-query and event-handling tasks ...

function handleVote({ target, currentTarget }) {
  const $target = $(target).closest('[data-vote-value]');

  if ($target.length >= 1) {
    $(currentTarget).off('click', handleVote);

    const $elmCount = $target.find('[data-vote-count]');
    const $elmIcon = $target.find('.icon');

    const count = parseInt($elmCount.data('voteCount') ?? 0, 10) + 1;

    $elmCount.data('voteCount', count)
    $elmCount.text(count);

    $elmIcon.addClass('fa').removeClass('far');
  }
}

$(() => {
  $('[data-voting]')
    .each((idx, rootNode) =>
      $(rootNode).on('click', handleVote)
    );
});
[data-voting] { margin: 10px; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>

<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>
<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>
<div data-voting>
  <button data-vote-value="like">
    <span>Like</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-up" aria-hidden="true"></i>
  </button>
  <button data-vote-value="dislike">
    <span>Dislike</span>
    <span data-vote-count="0">0</span>
    <i class="icon far fa-thumbs-down" aria-hidden="true"></i>
  </button>
</div>