0

I am new to js and I am trying to loop through elements by class name and make a change to the element's textcontent using javascript. The following is what I have so far, but this does not work.

<script type="text/javascript">
function dformat(i) {
    var hasDate = document.getElementsByClassName("dformat")[i].textContent.trim().includes("/");
    if(hasDate === false) {
       window.setTimeout(dformat, 100); /* this checks the flag every 100 milliseconds*/
    } else {
       dateString = document.getElementsByClassName("dformat")[i].textContent.trim();
       const options = { year: 'numeric', month: 'short', day: 'numeric' };
       let dateArr = dateString.split("/");
       var sMonth = dateArr[0];
       var sDay = dateArr[1];
       var sYear = dateArr[2];
       let newDateString = sYear + "/" + sMonth + "/" + sDay
       let newDate = new Date(newDateString);
       document.getElementsByClassName("dformat")[i].textContent=newDate.toLocaleDateString('en-us', options);
    }
}

var ditems = document.getElementsByClassName("dformat");
for (var i = 0; i < ditems.length; i++) {
  dformat(i);   
}
</script>

The following is an example of one of the elements

<span class="dformat">26/06/1992</span>

The expected output would be to have changed text on each of the elements with class dformat

Update: I tried the same function with getelementbyid on a single element (i.e without a for loop) and got the expected result. I have trouble doing the same on a set of elements by classname instead of a single element by id.

Vijayendar Gururaja
  • 752
  • 1
  • 9
  • 16
  • @Randy Casburn I tried the same function with getelementbyid on a single element (i.e without a for loop) and the date came out as valid with expected result. I have trouble doing the same on a set of elements by classname instead of single element by id. – Vijayendar Gururaja Jan 04 '22 at 19:44
  • 1
    I'm not sure I understand the motivation for the loop calling the function. If you intent is to deal with each and every one, why not just loop through the collection returned from `.getElementsByClassName()`? – Randy Casburn Jan 04 '22 at 19:51
  • `newDateString` holds invalid input for Date constructor when I test, how did you manage to construct a date with this, eg. '1992/26/06', plus what Randy said, you need for each like construct instead of having external for loop simply to pass indexes P.S. here is something that works - `let newDate = new Date(sYear, sMonth, sDay);` – Vitaliy Terziev Jan 04 '22 at 20:07

3 Answers3

1

My guess is that some value gives you an exception.

Try this:

var ditems = document.getElementsByClassName("dformat");
for (var i = 0; i < ditems.length; i++) {
  try {
    dformat(i);   
  }
  catch (error) {
    console.error(error);
  }
}

If it works, log the i in the exception to see the failing value.

Athanasios Kataras
  • 25,191
  • 4
  • 32
  • 61
1

setTimeout() will forget what index you are running dformat on. You should use setTimeout(dformat, 100, i) to pass on the parameter to the next call, so you can have multiple independent waits running at once.

UnsignedByte
  • 849
  • 10
  • 29
1

There's nothing wrong with your for loop, the problem is with your Date objection construction. Dates cannot be created with the YYYY/MM/DD format. (See warning section here) What you instead need to do is pass the year, month, and day as parameters to the constructor.

let newDate = new Date(sYear, sMonth, sDay);

Some other notes...

You don't need to use document.getElementsByClassName to find the elements each time. You can simply pass the element directly to the function.

function dformat(e) {
  dateString = e.textContent.trim();
  if (!dateString.includes('/')) {
    window.setTimeout(dformat, 100, e);
    return;
  }
  const options = { year: 'numeric', month: 'short', day: 'numeric' };
  let dateArr = dateString.split("/");
  var sMonth = dateArr[0];
  var sDay = dateArr[1];
  var sYear = dateArr[2];
  let newDate = new Date(sYear, sMonth, sDay);
  e.textContent=newDate.toLocaleDateString('en-us', options);
}

var ditems = document.getElementsByClassName("dformat");
for (var i = 0; i < ditems.length; i++) {
  dformat(ditems[i]);   
}

Here's a code snippet showing this in action.

function dformat(e) {
  dateString = e.textContent.trim();
  if (!dateString.includes('/')) {
    window.setTimeout(dformat, 100, e);
    return;
  }
  const options = { year: 'numeric', month: 'short', day: 'numeric' };
  let dateArr = dateString.split("/");
  var sMonth = dateArr[0];
  var sDay = dateArr[1];
  var sYear = dateArr[2];
  let newDate = new Date(sYear, sMonth, sDay);
  e.textContent=newDate.toLocaleDateString('en-us', options);
}

var ditems = document.getElementsByClassName("dformat");
for (var i = 0; i < ditems.length; i++) {
  dformat(ditems[i]);   
}
<span class="dformat">26/06/1992</span>
<br>
<span class="dformat">27/06/1992</span>
<br>
<span class="dformat">28/06/1992</span>
<br>
<span class="dformat">29/06/1992</span>

Secondly, if you use setTimeout, you will need to pass the element to the function. As you have it, the function will be called with no arguments, so it would break if it ever found something without a /. You can see how I did it above to fix that.

I'm a little suspicious of using setTimeout in this way. This only allows the spans to be formatted once, if you really want continuous monitoring then you you will need to constantly setTimeout outside the function definition in the main script. Or if you just need to wait for the document to be ready, you should place your scripts after your HTML content and that will not be a problem. (See here). I won't speculate further since that isn't the purpose of this question, but I wanted to let you know that something seems wrong there.

TechnoSam
  • 578
  • 1
  • 8
  • 23