Changing classes in Javascript

So here is my codepen --> https://codepen.io/Mike-was-here123/pen/qoVwxR

If you look on line 9 - 15, you have a function that should change the class from inputClass1 (CSS line 18) to inputClass2 (CSS line 30).

The whole purpose of this function is just to be able to change the alignment of the text based on what input box it is and where it is.

Now if you look on line 20 of my HTML, you can see that i try to, ontype, to activate that function with the current value of that input.

Only issue is, its not activating. I can tell this since i get no errors in the console, no “Hello” from line 10, which results in no change.

Why is this happening?

If I’m not mistaken it’s because there isn’t an ontype event handler—oninput is probably what you are after.

In addition, the argument that you are passing to the hourCheck() function has a typo:

document.getElementsById('hours').value // Extra 's'
document.getElementById('hours').value

If you fix both of those it should start working. :smile: On a somewhat related note—you can do all of this in the JavaScript pane, as your clock grows you will have more functions and having them mixed up in the HTML my become a bit of a hassle to maintain. If you are going to do that, it is possible to assign a function to an event handler with document.getElementById('hours').oninput = () => checkHour(val);, or, I think the more preferred way, with addEventListener('input', () => checkHour(val));

I hope that helps!

Thank you for the help.

Works now :smiley:

I did your preferred way.

Is there a way, instead of oninput, to do it when they type? This is because the function is always running because it has a default value. I wanted to change the other boxes when they click on another one and start typing.

Hmm, could you just quickly describe how you want them aligned? I think you could probably reoganise things around a bit without needing to add and remove classes—you could potentially even just depending on CSS Flexbox, too, or simply redesign your UI (if possible), depending on how you want things aligned.

I’m a bit confused about the bit about “chang[ing] the other boxes when [the user] click on another one and start typing”, but I think what you are looking for may be the onchange property (or Element.addEventListener('change', callback)). Also, it’s worth noting that you also change the styles of an element like this:

const element = document.getElementById('element');

element.style.textAlign = 'center';

I hope that helps!

Figured out a solution to checking the other boxes:

This code just checks the seconds box to see if its being typed in and changes the others. This code is on my codepen so you can go check it out and test.

–> https://codepen.io/Mike-was-here123/pen/qoVwxR?editors=0010

function zeroCheck(input1, id1, input2, id2) {
    if (document.getElementById(id1).value.length < 2 === true) {
      return (document.getElementById(id1).value = "0" + Number(input1));
    }
    if (document.getElementById(id2).value.length < 2 === true) {
      return (document.getElementById(id2).value = "0" + Number(input2));
    }
  }
  var hours = document.getElementById("hours").value;
  var minutes = document.getElementById("minutes").value;
  var seconds = document.getElementById("seconds").value;
  setInterval(function() {
    if (document.getElementById("seconds").value !== seconds) {
      console.log("seconds");
      for (var i = 0; i <= 2; i++) {
        zeroCheck(
          document.getElementById("minutes").value,
          "minutes",
          document.getElementById("hours").value,
          "hours"
        );
      }
      seconds = document.getElementById("seconds").value;
    }
    if (document.getElementById("minutes").value !== minutes) {
      for (var i = 0; i <= 2; i++) {
        zeroCheck(
          document.getElementById("hours").value,
          "hours",
          document.getElementById("seconds").value,
          "seconds"
        );
      }
      minutes = document.getElementById("minutes").value;
    }
    if (document.getElementById("hours").value !== hours) {
      for (var i = 0; i <= 2; i++) {
        zeroCheck(
          document.getElementById("minutes").value,
          "minutes",
          document.getElementById("seconds").value,
          "seconds"
        );
      }
      hours = document.getElementById("hours").value;
    }
  }, 1);

Here is how it works:

I get the initial value of each of the items

 var hours = document.getElementById("hours").value;
  var minutes = document.getElementById("minutes").value;
  var seconds = document.getElementById("seconds").value;

Then i do a set-interval loop of 1 millisecond.

setInterval(function() {  // 14
   // code 
}, 1); // 51

Lets look at the seconds (3rd box on the right w/ codepen).

 if (document.getElementById("seconds").value !== seconds) {

Each iteration i check the saved value of it vs the current value. If the values aren’t the same, i know i have edited that box.

If that is true:

for (var i = 0; i <= 2; i++) {
        zeroCheck(
          document.getElementById("minutes").value,
          "minutes",
          document.getElementById("hours").value,
          "hours"
        );
      }

This run the other boxes (minutes, hours) through the zeroCheck() function to check if i can add 0’s because i know they are not being edited.

In ZeroCheck() function:

if (document.getElementById(id1).value.length < 2 === true) {
      return (document.getElementById(id1).value = "0" + Number(input1));
    // adding 0 plus its current single digit value "0"+"5" = "05"
 }

This is just one of the if statements. id1 would be minutes and input1 would be its value. Now i see if the length of the value is < 2. If that is true, i add a 0 plus its current single digit value, "0"+"5" = "05"or "0"+"0" = "00".

It only does this to the boxes your not typing in.

After the loop is done:

seconds = document.getElementById("seconds").value;

Now, after checking all the other boxes, it resets the saved value to the current thing you have typed in. Now if your typing multiple characters, it just does this multiple times (Running the values of the other boxes, etc)

This is done with every box, so that is why the code is so long. Its just three of the same thing (seconds, minutes, hours).

This makes sure everything saves. Also so the other boxes are only checked once, instead of infinity and cause them to be impossible to edit.

Hope this made sense, please reply and ask questions if they don’t. I’m also learning how to type correctly, so i’m sorry if sentences were fragmented or short.

Thank you for taking the time to explain your code. :smile:

Things read just fine, I don’t think there is anything to worry about with your writing. If anything, I prefer shorter sentences that make sense, which is what you are doing. Other than the occasional typo (which we all have!) the only thing that I would pick on is the difference between “you’re” and “your”.

Anyhow, back to your code—it does work but I don’t think it’s ideal, particularly because of this:

setInterval(function() {
   // code 
}, 1);

The first thing to note is that the delay of setInterval() has a lower limit of 10 ms, from the documentation:

The time, in milliseconds (thousandths of a second), the timer should delay in between executions of the specified function or code. If this parameter is less than 10, a value of 10 is used. Note that the actual delay may be longer; see Reasons for delays longer than specified in WindowOrWorkerGlobalScope.setTimeout() for examples.

The second thing is that even if the delay is set to 10ms, you are still running the code inside the block fairly rapidly. While it may not be a problem in your case (and in cases where it’s necessary, such as when you are writing a game loop—but you probably should use requestAnimationFrame instead of setInterval in that case), since there isn’t a huge amount of code to run, it’s something to keep in mind for when you build more complex things.

Just so that we are on the same page—what you want to achieve is that you want to zero-pad the inputs when a user changes the number. If that is indeed the case, then I think a more elegant (and appropriate way) to handle this is something similar to the code below (see comments):

// We are modifying zeroCheck to handle an input event trigger by
// one of the input elements. The function will be used as a callback for
// addEventListener, an event object is passed to it as an argument when
// it is called:

function zeroCheck(event) {
  // The target is the element that triggers the event in this case
  // For example, if the element that triggers the input event is 
  // the one with id="hours", event.target will be the same as
  // document.getElementById('hours');
  const element = event.target;
  // Converting the value to a number removes any leading zeros
  // For example, if the value is '01' and the user types '2',
  // you will end up with '012'
  const currentValue = Number(element.value);

  // Values of less than 10 are single digits, hence this change
  if (currentValue < 10) {
    element.value = "0" + currentValue;
  }
  else {
    element.value = currentValue;
  }
}

['hours', 'minutes', 'seconds'].forEach((id) => {
  // Get the corresponding DOM element:
  const element = document.getElementById(id);
  
  // Add an event listener for handling the oninput event to each of them
  // Passing the function zeroCheck in as the callback
  element.addEventListener('input', zeroCheck);
});

If you are are doing something very similar (or the same) for different components, there is usually room for refactoring.

I hope that’s helpful and is what you are looking for! Don’t forget to improve zeroCheck to handle cases where the user inputs unwanted values!

Its only supposed to do it do the two that are not being typed in. How i this code are you able to tell which ones are are not being typed in?

It doesn’t—because it seems that what I suggested isn’t what you want again. :sweat:

Having said that, changing the function zeroCheck will do (I think) what you want:

const ids = ['hours', 'minutes', 'seconds'];

function zeroCheck(event) {
  const currentId = event.target.id;

  ids.forEach((id) => {
    if (id !== currentId) {
      const element = document.getElementById(id);
      const currentValue = Number(element.value);
      
      if (currentValue < 10) {
        element.value = "0" + currentValue;
      }
      else {
        element.value = currentValue;
      }  
    }
  ));
}

['hours', 'minutes', 'seconds'].forEach((id) => {
  const element = document.getElementById(id);
  
  element.addEventListener('input', zeroCheck);
});

Even though this is probably want you want, I still don’t think that it’s an ideal way to do it because it seems to be a lot of unnecessary work for an atypical UI change. Perhaps you should consider using onblur instead, so every time someone finishes typing and move the focus away from the input it will run your code.

I don’t know, i feel like my code is pretty simple, just repetitive. It also opens up the gate for me to do anything i want with any of the inputs.

Onblur looks pretty cool, ill use that in future code.

I’m glad that there is something useful from there! Good luck with the rest of it! :smile:

1 Like