How to stop the default behavior of a link from a child element



I am making an ad card using bootstrap, I’ve placed the card inside <a> element so that when the user clicks the card, the whole card will act as a link.

I have a favorite link that marks the ad as a favorite for the user. The problem is, since the whole card is a link, whenever I click the fav button, the parent link is clicked which is meant to go to the page of the ad, I want to stop that behavior and instead run whatever function favorite button has assigned to it.

Basically I want to stop the behavior of parent link.

My HTML for card:

<a href="" class="text-dark text-decoration-none">
    <div class="card">
        <div class="card-img-top">
            <img src="{{ url_for('static', filename='images/test/table-with-various-goods-in-shop.jpg') }}" alt="Card Title">
        </div>
        <div class="card-body">
            <h5 class="card-title">Price</h5>
            <h6 class="card-subtitle mb-2 text-muted">Title</h6>
            <span class="fa fa-heart-o favorite-ad-card-btn" id="favoriteAdCardBtn"></span>
            <div class="footer mt-4">
                <small class="float-start text-muted">Location</small>
                <small class="float-end text-muted">Time</small>
            </div>
        </div>
    </div>
</a>

My JS present in an external file:

let favoriteBtn = document.getElementById("favoriteAdCardBtn");
favoriteBtn.addEventListener("mouseover", mouseOverFavoriteBtn);
favoriteBtn.addEventListener("mouseout", mouseOutFavoriteBtn);
favoriteBtn.addEventListener("click", onClickFavoriteBtn);

function mouseOverFavoriteBtn(e) {
    if (e.target.classList.contains("fa-heart-o")) {
        e.target.classList.remove("fa-heart-o");
        e.target.classList.add("fa-heart");
    }
}

function mouseOutFavoriteBtn(e) {
    if (!(e.target.classList.contains("text-danger"))) {
        e.target.classList.remove("fa-heart");
        e.target.classList.add("fa-heart-o");
    }
}

function onClickFavoriteBtn() {
    e = window.event || e;
    if (this == e.target) {
        console.log("Fav btn clicked");
    }
}

Answer

You can prevent the default event if the button is clicked inside the anchor element’s click event handler function like the following way:

document.querySelector('a.text-dark.text-decoration-none').addEventListener("click", function(e){
   if(e.target.id == 'favoriteAdCardBtn'){
     e.preventDefault();
   }
});

Demo:

let favoriteBtn = document.getElementById("favoriteAdCardBtn");
favoriteBtn.addEventListener("mouseover", mouseOverFavoriteBtn);
favoriteBtn.addEventListener("mouseout", mouseOutFavoriteBtn);
favoriteBtn.addEventListener("click", onClickFavoriteBtn);

function mouseOverFavoriteBtn(e) {    
    if (e.target.classList.contains("fa-heart-o")) {
        e.target.classList.remove("fa-heart-o");
        e.target.classList.add("fa-heart");
    }
}

function mouseOutFavoriteBtn(e) {
    if (!(e.target.classList.contains("text-danger"))) {
        e.target.classList.remove("fa-heart");
        e.target.classList.add("fa-heart-o");
    }
}

function onClickFavoriteBtn(e) {
    e = window.event || e;
    if (this == e.target) {
        console.log("Fav btn clicked");
    }
}

document.querySelector('a.text-dark.text-decoration-none').addEventListener("click", function(e){
  if(e.target.id == 'favoriteAdCardBtn'){
    e.preventDefault();
  }
});
<link href="http://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
<a href="" class="text-dark text-decoration-none">
    <div class="card">
        <div class="card-img-top">
            <img src="{{ url_for('static', filename='images/test/table-with-various-goods-in-shop.jpg') }}" alt="Card Title">
        </div>
        <div class="card-body">
            <h5 class="card-title">Price</h5>
            <h6 class="card-subtitle mb-2 text-muted">Title</h6>
            <span class="fa fa-heart-o favorite-ad-card-btn" id="favoriteAdCardBtn"></span>
            <div class="footer mt-4">
                <small class="float-start text-muted">Location</small>
                <small class="float-end text-muted">Time</small>
            </div>
        </div>
    </div>
</a>


Source: stackoverflow