2

I'm trying to set up a function on my page that will iterate through a series of news titles and will strikethrough the title if it contains a specific marker.

The titles are pulled from a WordPress post database and displayed within a <div> with ID="news". Each specific title is a link <a> with class="title". I am first collecting all the titles into an array so I can iterate through them but for some reason, it doesn't work properly.

My code is:

<script type="text/javascript">
//first I create the array
var test_DOM = document.getElementById("news").getElementsByClassName("title");
//then I set the marker
var marker = "[X]"
//then I wirte the function to iterate through the array://
function checkX() {
for(i in test_DOM){
if (i.includes(marker)) {
i = "<del>", i, "</del>";
console.log(test_DOM[i], "changed")
}
console.log(test_DOM[i].innerHTML)
}
};
//and I call the function
checkX()

An expected result would be to see the list of news to change from

[x] News 1
[EG] News 2

to

[x] News 1 <- struck through
[EG] News 2 <- still the same

However, nothing happens. I know that getElementsByClassName gets me an array of objects and that I should use .innerHTML and some point, but using it inside the function like

function checkX() {
for(i in test_DOM){
if (i.innerHTML.includes(marker)) {
i.innerHTML = "<del>", i, "</del>";
console.log(test_DOM[i], "changed")
}
console.log(test_DOM[i].innerHTML)
}
};

gives an error of "TypeError: i.innerHTML is undefined"

Amin.MasterkinG
  • 793
  • 1
  • 12
  • 23
Kik4s
  • 23
  • 3
  • 2
    `i = "", i, "";` does not combine the strings, it uses the [comma operator](https://stackoverflow.com/questions/3561043/what-does-a-comma-do-in-javascript-expressions) and returns only the last item. Also `i` would not be each *value* of `test_DOM` but each *key* from `test_DOM`. – VLAZ May 15 '19 at 08:28
  • Did you check for errors in the browser console? Also, getElementsByClassName() returns a "NodeList" object, not an array as may be expected, so running a for in loop over it will run over all properties, not just item indexes. The NodeList object does contain a length property that corresponds to the length of indeces though, so a notmal for loop like this should work. `for (i = 0; i < test_DOM.length; i++)` – 2pha May 15 '19 at 08:59
  • @Kik4s Hey, just a friendly reminder to mark an answer as "accepted" if it answered your question. If the 2 existing answers below don't quite answer your question, you can leave a comment to get some more clarification. Thanks! – jackrugile May 21 '19 at 20:52

2 Answers2

0

I think I understand what you're trying to do and have a solution for you. There are a few things that need to be updated to get the script working as you described:

  1. Your DOM query can be shortened using document.querySelectorAll.
  2. When using String.includes(), it is case sensitive. This means that [x] is not equal to [X], so be sure to check the correct version for your test.
  3. As @2pha mentioned in their comment, test_DOM will return a NodeList, which is an array-like structure, but not quite an array. To convert it to an array that can be looped over as you intend, you can use [].slice.call(test_DOM).
  4. As @VLAZ mentioned in their comment, the comma operator will not combine strings. You should use the 'string' + 'string' syntax to combine strings.
  5. Also as @VLAZ mentioned, the for...in loop is more intended for objects, in which it will output the keys of the object, not the values, so this isn't appropriate for your use case. You can use Array.forEach() instead.
  6. As you alluded to, the Array.includes() check should use innerHTML for its check, and innerHTML should be used to then reset the value of the element.

I included a snippet below with some modifications.

var test_DOM = document.querySelectorAll('#news .title');
var marker = '[x]';

function checkX() {
  [].slice.call(test_DOM).forEach(function(elem, i) {
    if(elem.innerHTML.includes(marker)) {
      elem.innerHTML = '<del>' + elem.innerHTML + '</del>';
    }
  });
}

checkX()
<div id="news">
  <h1 class="title">[x] News 1</h1>
  <h1 class="title">[EG] News 2</h1>
</div>
jackrugile
  • 1,583
  • 12
  • 25
0

Follow on from my comment.
ie. "getElementsByClassName() returns a "NodeList" object, not an array as may be expected". But does have a length property.
The minimal changes to your code to get it to work.

//first I create the array
var test_DOM = document.getElementById("news").getElementsByClassName("title");
//then I set the marker
var marker = "[X]"
//console.log(test_DOM);
//then I wirte the function to iterate through the array://
function checkX() {
  for(i = 0; i < test_DOM.length; i++){
    if (test_DOM[i].innerHTML.includes(marker)) {
      test_DOM[i].innerHTML = "<del>" + test_DOM[i].innerHTML + "</del>";
      //console.log(test_DOM[i], "changed")
    }
    //console.log(test_DOM[i].innerHTML)
  }
};
//and I call the function
checkX();
<div id="news">
<h2 class="title">title 1</h2>
<h2 class="title">[X]title 2</h2>
<h2 class="title">title 3</h2>
</div>
2pha
  • 8,257
  • 2
  • 25
  • 38
  • jackrugile's answer is more indepth than mine (an probably should be the accepted answer), but mine should allow you to easily understand why your specific code was not working. – 2pha May 15 '19 at 09:11