How to re-enable click behaviour after preventdefault?

So first I have a script that checks for the class .current and adds prevent default for all the links without that class, and then a callback that should re-check and restore click behavior for the link with the new .current class while applying prevent default to the other links.

  1. Initial script that sets prevent default
var links = document.querySelectorAll('.link-slider');
var slidesv = document.querySelectorAll('.carousel-3d-slide');

for (var i = 0; i < slidesv.length; i++) {

    var slidev = slidesv[i];
    var link = links[i];

if (slidev.classList.contains('current')) {
    // Class exists!
     /*console.log("works");
     link.addEventListener("click", function(e){ return true; });*/
     
      } else {
          // Class not found
      console.log("nonworks");
      link.addEventListener("click", function(e) {

    e.preventDefault(); }, false); 
}             
}
  1. callback
onAfterSlideChange() {
      var linkscallback = document.querySelectorAll('.link-slider');
      var slidescallback = document.querySelectorAll('.carousel-3d-slide');

      for (var i = 0; i < slidescallback.length; i++) {

          var slidecallback = slidescallback[i];
          var linkcallback = linkscallback[i];
          linkcallback.addEventListener("click", function(e){ return true; });
           console.log("true");
      if (slidecallback.classList.contains('current')) {
          // Class exists!              
            } else {
            linkcallback.addEventListener("click", function(e) {
          e.preventDefault(); }, false); 
      }             
      }
    },

Probably what you should do here is just have one event listener for all of those links and then put your logic in that event listener. This is referred to as event delegation.

Thank you. I’m not making sense though at the example in the link :confused: on the logic steps, but I’m on it. This example I mean:

let selectedTd;

table.onclick = function(event) {
  let target = event.target; // where was the click?

  if (target.tagName != 'TD') return; // not on TD? Then we're not interested

  highlight(target); // highlight it
};

function highlight(td) {
  if (selectedTd) { // remove the existing highlight if any
    selectedTd.classList.remove('highlight');
  }
  selectedTd = td;
  selectedTd.classList.add('highlight'); // highlight the new td
}

Something like this? Tried to replicate the example… Didn’t change the variable names for now…

Update: improved the code

let selectedTd;
let table = document.getElementsByClassName('carousel-3d-slider');
table.onclick = function(event) {
  let target = event.target; // where was the click?

  while (target != this) {
        if (target.tagName == 'a') {
          highlight(target);
          return;
        }
        target = target.parentNode;
      }
  }

function highlight(node) {
      if (selectedTd) {
        selectedTd.classList.remove('preventcurrent');
        //
      }
      selectedTd = a;
      selectedTd.classList.add('preventcurrent');

      if (selectedTd.classList.contains('preventcurrent')) {
          event.preventDefault();
  }
    }

This is pretty much the only front missing to get this to work, so pls can you tell me if I’m closer or a bit of guidance? I am a bit lost

Okey how about this code…

var linkscallback = document.querySelectorAll('.link-slider');
var slidescallback = document.querySelectorAll('.carousel-3d-slide');


linkscallback.addEventListener('click', function (event) {
  for (var i = 0; i < slidescallback.length; i++) {

          var slidecallback = slidescallback[i];
          var linkcallback = linkscallback[i];


      if (slidecallback.classList.contains('current')) {
        linkcallback.classList.remove('preventcurrent');                        
            } else {
       linkcallback.classList.add('preventcurrent');
          event.preventDefault();
      }
      if (linkcallback.classList.contains('preventcurrent'))
        linkcallback.preventDefault();
      }
  }

Edit : Version 6 branch 2 which is not working…

var slidesv = document.querySelectorAll('.carousel-3d-slide');

linksv.forEach(el => el.addEventListener('click', event => {
 
//event.target.classList.remove('preventcurrent');

for (var i = 0; i < slidesv.length; i++) {

          var slidev = slidesv[i];
          //var linkv = linksv[i];

  if (slidev.classList.contains('current')) {
      event.target.classList.remove('preventcurrent');
      }             
     else {       
      event.target.classList.add('preventcurrent');
      };

  if (event.target.classList.contains('preventcurrent')) {
    event.target.preventDefault();
      }
}
}));

Alright this code works!!! It has one issue though each time I click on the selected item in the carousel it disappears!!! o.o


let dropDowns = Array.from(document.querySelectorAll('.link-slider'));
let slide = Array.from(document.querySelectorAll('.carousel-3d-slide'));

const handleClick = (e) => {
  e.preventDefault();
  slide.forEach(node => {
    node.classList.remove('current');
  });
  e.currentTarget.classList.add('current');

}

dropDowns.forEach(node => {
  node.addEventListener('click', handleClick)
});

Okey I did some progress, but I’m gertting the error “Uncaught TypeError: slidev.classLists is undefined”. Alright why I feel like I’m the only one checking this thread, won’t post again…

let links = Array.from(document.querySelectorAll('.link-slider'));
let slides = Array.from(document.querySelectorAll('.carousel-3d-slide'));

const handleClick = (e) => {
  e.preventDefault();
  //const active = document.querySelector('.current');
  slides.forEach(slidev => {

    if(slidev.classLists.contains('current')){
    e.currentTarget.classList.add('current');
  }
  else {
    slidev.classList.remove('current');
  }
  });
  
  //e.currentTarget.classList.add('current');
}

links.forEach(link => {
  link.addEventListener('click', handleClick)
});

There’s a small typo - the method is .classList, you’ve written .classLists.

It’s very difficult to debug your code just from looking at it here, that’s probably why so few are answering. I also can’t copy/paste your code into my own editor to see what’s wrong with it, because I don’t have your HTML. Can you maybe put your code on an online sandbox like codepen.com?

1 Like

Thank you!

Corrected! :slight_smile:

Thanks for explaining!

Sure! Will do it asap :wink:

Alright here it is:
https://codepen.io/Snippet/pen/NWbgYdR

Awesome, thanks. Now there’s not much happening yet, in fact I’m not sure what’s supposed to happen. Are the slider links actual links to an external resource, where the user should be able to click on (you have wikipedia for example), or should they allow the user to flip through a slideshow? When should the link be active (no preventDefault), and when should it be disabled?

I also don’t understand your click handler, because

  • the slides are the <div> elements with class .carousel-3d-slide
  • the e.currentTarget however is the <a> that received the click event

Which one should have the class “current”, the div or the link?


I tried to guess what’s supposed to happen and modified your code a little so that

  • when I click on a link, the corresponding slide-<div> gets the .current class, not the link
  slides.forEach((slide) => {
    slide.classList.remove("current");
    e.target.closest('.carousel-3d-slide').classList.add("current");
  });

Not sure if that helps though. Can you give some more info?

1 Like

Sure! Thank you!! I explain in more detail: I’m trying to disable clicks on a slider for the not active slides. In other words the active slide contains a class named: “current” and only this slide, the active one, should have click behavior restored/not prevented.

The div, but perhaps it should be better to add the class to the link inside/contained in the div instead, that has the .current class, and then allow for click behavior to the link that has the class “current”.

Unfortunately removing the .current class for all slides and then adding it for the active slide is not an option since removing the .current class from the active slide causes it to disappear. Do note the codepen is a lot simplified, hence that won’t happen but it does in the local server installation, so I guess it would have to be used another approach.

Maybe this should be added

 links.forEach(link => {
  if(link.classList.contains('current')){
  link.addEventListener('click', handleClick)
}
});

I’m still not 100% sure how the user is supposed to flip through the slideshow, but maybe that’s not important.

You could give the current class to the links, and then first check if the link has current. If it doesn’t, then preventDefault, otherwise, the link will work:

const handleClick = (e) => {
  if (!e.target.classList.contains('current') {
      e.preventDefault()
  }
...
}

This however is a bad idea:

Because you can either run that once (on page load), and then only one link would get an event listener. Or you can run it each time someone clicks on a link, and then you’d add more and more event listeners to the links (while at the same time not removing them where you don’t want a click event to have an effect).

The best solution is to give them all an event listener, and within the callback, first check if the link has a certain class or not, and then decide which code should run for an active/unactive link.

1 Like

Ah I explain: clicking in a non active slide should make that slide the current/active one.

Okey!

Give some time to try/play with the code… :wink:

Okey something like this? it is preventing all clicks now though, so I’m doing something wrong here.

let links = Array.from(document.querySelectorAll('.link-slider'));
var linksv = document.querySelectorAll('.link-slider');
let slides = Array.from(document.querySelectorAll('.carousel-3d-slide'));

const handleClick = (e) => {
  //e.preventDefault();
  links.forEach(link => {
    link.classList.remove('current');
  });

  slides.forEach(slide => {
    if (slide.classList.contains('current')){
    e.currentTarget.classList.add('current');
  }

  if (!e.target.classList.contains('current')) {
      e.preventDefault()
  }

  });
}

links.forEach(link => {
  link.addEventListener('click', handleClick)
});

Edit will fork the codepen: https://codepen.io/Snippet/pen/JjbJBYB
Edit 2 : improved the code more
I can see that even with the current class, the link click is prevented.

Edit: using the console in case is remotely useful…

if (!e.target.classList.contains('current')) {
      e.preventDefault();
      console.log(e);
  }
i get this

The preventDefault needs to happen at the very top of the function, because right now, you first loop over your links and remove the current class from all of them, and after that, you check for the current class. So you’d rather do something like this (I’ve added comments to explain):

const handleClick = (e) => {
  if (!e.target.closest("a").classList.contains("current")) {
    e.preventDefault();
  }

  /* remove current class from all links, and give it only to the one that was clicked */
  links.forEach((link) => link.classList.remove("current"));
  e.target.closest("a").classList.add("current");


  /* remove current class from all slides, and give  it only to the slide that was clicked*/
  slides.forEach((slide) => slide.classList.remove("current"));
  e.target.closest(".carousel-3d-slide").classList.add("current");
};

1 Like