I want to disable a section of javascript when a certain class is on the `html` element - and enable again once removed

In short I have a section of script that works perfectly. However I have a ‘theme’ toggle which when clicked adds a class to the html element so I can change the page styling. On a particular class - .retro in the example - I do something else with the cursor so I need to disable the script. Then when the toggle removes that class, run it again.

I thought I could do this using an if statement and an EventListener but I’m having some (a lot) of trouble and would appreciate some help.

I need to do this in a couple of places but I think I’d start with the “Custom Cursor” bit in the JS. Which looks like:

var cursor = document.querySelector(".cursor");
var cursorTrail = document.querySelector(".cursor-trail");
var a = document.querySelectorAll("a");
var timeout;

window.addEventListener(
  "mousemove",
  function (e) {
    var x = e.clientX;
    var y = e.clientY;
    if (!timeout) {
      timeout = setTimeout(function () {
        timeout = null;
        cursor.style.transform = `translate(${x - 2}px, ${y - 2}px)`;
        timeout = null;
        cursorTrail.style.transform = `translate(${x - 16}px, ${y - 16}px)`;
      }, 16);
    }
  },
  false
);

/* Add/Remove Classes */

document.addEventListener("mousedown", function () {
  cursor.classList.add("cursor--click");
});

document.addEventListener("mouseup", function () {
  cursor.classList.remove("cursor--click");
});

a.forEach((item) => {
  item.addEventListener("mouseover", () => {
    cursorTrail.classList.add("cursor-trail--hover");
  });
  item.addEventListener("mouseleave", () => {
    cursorTrail.classList.remove("cursor-trail--hover");
  });
});

a.forEach((item) => {
  const interaction = item.dataset.interaction;

  item.addEventListener("mouseover", () => {
    cursor.classList.add(interaction);
  });
  item.addEventListener("mouseleave", () => {
    cursor.classList.remove(interaction);
  });
});

I tried using something like this…

if(document.getElementsByTagName('html').classList.contains('retro')) {
    // Script to run only have `.retro` is on the `html` tag
}

and this…

var runJS = document.getElementsByTagName('html');
if(runJS.classList.contains('retro')) {
    alert("Hello! I am an alert box!!");
 }

But I’m obviously getting something fundamentally wrong. Regular contributors might recognise this layout in my CodePen! But no issues with the styling/script as such. Just want to update so the script is disabled when a class is added to the html.

It is worth nothing I’ll have a bit of code which I’m yet to add which is essentially the opposite, so isn’t needed unless .retro is on the html. So I guess it would help if the solution can work both ways.

CodePen: https://codepen.io/moy/pen/mdLWEyO

Thanks in advance!

Well first whenever I have some code that needs to be enabled or disabled, I think about creating a function. One that can enable the code and one that can disable it. Especially when I need to toggle something.

I am not 100% sure what you are exactly trying to enable or disable in the script, but if you have a function that adds your event handlers and such, then create another that removes the handlers added in the first function, then you have a situation where you can simply call one function whenever you need to enable the code and another to disable it.

For your cursor code there, you are adding some event handlers. See if you can put that into its own function. Then in another function have it remove those handlers. You are essentially going to distill your build up and tear down code into their own single functions that can then be easily reasoned about.

Hopefully you get what I am saying and that I am understanding what you are trying to do here. :slight_smile:

1 Like

Hi @moymadethis, the issue here is that getElementsByTagName() returns a collection, not a single element; so you’d need to check

if (document.getElementsByTagName('html')[0].classList.contains('retro')) {
    // Script to run only have `.retro` is on the `html` tag
}

… or just use querySelector('html') which does only return the first match. Or, in this case, access the root element directly via documentElement:

if (document.documentElement.classList.contains('retro')) {
    // Script to run only have `.retro` is on the `html` tag
}

Another option would be to simply hide the cursor trail with CSS like so:

.retro .cursor-trail {
  display: none;
}
1 Like

Hi guys, thanks for this! Hopefully this gives more context…

I actually thought I could just hide the divs with CSS and it’d be fine running in the background without disabling the script - however I’m actually trying to make the divs on the page ‘draggable’ when the .retro class is added.

I haven’t decided on the approach/script for that yet but when I was trying to use https://draggabilly.desandro.com I noticed the script wouldn’t work unless I deleted the cursor JS. I’ve since found this CodePen that I may try and use instead.

But I guess like the cursor, I’d need to disable this script when the class is removed as I won’t want the blocks to be moved around unless it’s on that class.

@m3g4p0p with this approach it looks like I could wrap my entire block of code in the 2 examples you’ve given. And it would only run when the class of .retro is added to the html tag. I guess I could need to include an ‘on click’ listened to check/update if a class change occurred?

@Martyr2 hopefully the above provides more context - but it sounds like you’re saying I might be better off just targeting a section of the script rather than disabling/enabling the entire block? Sounds like that might be better for loading times and not reloading the entire script again?

Swiper JS

One final thing - to give the full picture of what I’m trying to do as this question might follow haha is…

My Swiper instance works fine but when .retro is added I want to change the autoplay, effect and loop settings to be different values. I’m not sure if I would need to duplicate the entire block, emend those setting and have each block in and if/else statement. Or if it’s possible to target like…

if (document.documentElement.classList.contains('retro')) {
  const swiper = new Swiper('.swiper', {
    effect: 'slide',
    loop: true,
    autoplay: false,
    scrollbar: {
      el: '.swiper-scrollbar',
      draggable: true
    }
  });
}

Sorry for the long reply. I hope that gives a bit more context and a overall picture of my end-goal - and hasn’t confused things!

Yes you might add / remove the cursor-specific event listeners inside the function where you toggle the theme classes… i.e.

function handleMouseMove (event) {
  // Set cursor transform
}

if (html.classList.contains('retro')) {
  // Same for mousedown / mouseup
  window.addEventListener('mousemove', handleMouseMove)
} else {
  window.removeEventListener('mousemove', handleMouseMove)
}

Another option would be to return early from the event handlers in case you have the .retro class added; or use a higher order wrapper function that will conditionally call the event handler depending on the theme class:

function createHandler (callback) {
  return function (event) {
    if (!document.documentElement.classList.contains('retro') {
      callback.call(this, event)
    }
  }
}

window.addEventListener('mousemove', createHandler(function (event) {
  // This will only get executed when there is no .retro class on the
  // html element; otherwise set transorms here etc. as before
}))
1 Like

Thanks, theses look great! I’m just heading out but I’ll have a play around with both of these later. I’ll let you know how I get on but this looks like it’s a massive help.

With the first I guess that makes it easy to reuse so I enable/disable different sections of script for if the class is added or not - I can just flip the addEventListener and removeEventListener around to basically do the opposite.

Out of interest, for the 2nd, is there an equivalent of “does not contain”? Or would you need to specify a different class - or no class at all?

Why just use the ! (NOT) operator… i.e.

if (!myElement.classList.contains('some-class')) { /* ... */ }

In your dragstart event check if the element is a div with the retro class and only proceed with the drag if the conditions are met.
There are several tutorials on dragging and repositioning elements. Learn how to drag and drop elements and your problem disapears.

Thanks for all this! Finally getting some time to have a play around with the suggestions! Just using the cursor as the example for now…

I tried the 2nd example which worked when .retro was applied to the HTML tag on load.

But when I cycled through the classes, so .retro was removed and the script ran as intended (yey!) but when .retro was re-applied the script continued to run rather than stopping? But otherwise seems like this might be the one…

function createHandler(callback) {
     return function(event) {
         if (!document.documentElement.classList.contains('retro')) {
             callback.call(this, event)
         }
     }
 }
 
 window.addEventListener('mousemove', createHandler(function(event) {
 
     var cursor = document.querySelector(".cursor");
     var cursorTrail = document.querySelector(".cursor-trail");
     var a = document.querySelectorAll("a");
     var timeout;
 
     window.addEventListener(
         "mousemove",
         function(e) {
             var x = e.clientX;
             var y = e.clientY;
             cursor.style.transform = `translate(${x - 2}px, ${y - 2}px)`;
             if (!timeout) {
                 timeout = setTimeout(function() {
                     timeout = null;
                     cursorTrail.style.transform = `translate(${x - 16}px, ${y - 16}px)`;
                 }, 24);
             }
         },
         false
     );
 
     /* Add/Remove Classes */
 
     document.addEventListener("mousedown", function() {
         cursor.classList.add("cursor--click");
     });
 
     document.addEventListener("mouseup", function() {
         cursor.classList.remove("cursor--click");
     });
 
     // a.forEach((item) => {
     let links = document.querySelectorAll('a, #hello');
     links.forEach((item) => {
         item.addEventListener("mouseover", () => {
             cursorTrail.classList.add("cursor-trail--hover");
         });
         item.addEventListener("mouseleave", () => {
             cursorTrail.classList.remove("cursor-trail--hover");
         });
     });
 
     a.forEach((item) => {
         const interaction = item.dataset.interaction;
 
         item.addEventListener("mouseover", () => {
             cursor.classList.add(interaction);
         });
         item.addEventListener("mouseleave", () => {
             cursor.classList.remove(interaction);
         });
     });
 }))

The first one (below) didn’t seem to enable/disable the script at all when the class was added/removed so I just wanted to check I had used this correctly.

function handleMouseMove(event) {

   var cursor = document.querySelector(".cursor");
   var cursorTrail = document.querySelector(".cursor-trail");
   var a = document.querySelectorAll("a");
   var timeout;

   window.addEventListener(
       "mousemove",
       function(e) {
           var x = e.clientX;
           var y = e.clientY;
           cursor.style.transform = `translate(${x - 2}px, ${y - 2}px)`;
           if (!timeout) {
               timeout = setTimeout(function() {
                   timeout = null;
                   cursorTrail.style.transform = `translate(${x - 16}px, ${y - 16}px)`;
               }, 24);
           }
       },
       false
   );

   /* Add/Remove Classes */

   document.addEventListener("mousedown", function() {
       cursor.classList.add("cursor--click");
   });

   document.addEventListener("mouseup", function() {
       cursor.classList.remove("cursor--click");
   });

   // a.forEach((item) => {
   let links = document.querySelectorAll('a, #hello');
   links.forEach((item) => {
       item.addEventListener("mouseover", () => {
           cursorTrail.classList.add("cursor-trail--hover");
       });
       item.addEventListener("mouseleave", () => {
           cursorTrail.classList.remove("cursor-trail--hover");
       });
   });

   a.forEach((item) => {
       const interaction = item.dataset.interaction;

       item.addEventListener("mouseover", () => {
           cursor.classList.add(interaction);
       });
       item.addEventListener("mouseleave", () => {
           cursor.classList.remove(interaction);
       });
   });
}

if (html.classList.contains('retro')) {
   // Same for mousedown / mouseup
   window.addEventListener('mousemove', handleMouseMove)
} else {
   window.removeEventListener('mousemove', handleMouseMove)
}

I get some errors about event is defined but never used - is that something I can take out?

Thanks again for all this!