1

I'm trying to remove certain elements from a html-table using javascript. More specifically I need to remove all the nodes that do not contain two substrings.

I've tried to save the positions of the nodes that do not contain these two substrings and then remove them.

var posToRemove = [];
var tbody = document.getElementsByTagName("tbody")[0];
var trTags = tbody.getElementsByTagName("tr");
var substring0 = "Foo";
var substring1 = "Bar";

for (i = 0; i < trTags.length; i++) {
  var trTextContent = trTags[i].textContent;
  if (trTextContent.indexOf(substring0) !== -1 || trTextContent.indexOf(substring1) !== -1) {
    //do something
  } else {
    posToRemove.push(i);
  }
}

for (i = 0; i < posToRemove.length; i++) {
  trTags[posToRemove[i]].remove();
}
<table>
  <tbody>
    <tr>
      <td><a href="https://example.org">Foo</a></td>
      <td>Description</td>
      <td>2019</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Test</a></td>
      <td>Description</td>
      <td>2018</td>
    </tr>
    <tr>
      <td>Bar</td>
      <td>Description</td>
      <td>2017</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Foo</a></td>
      <td>Description</td>
      <td>2019</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Test</a></td>
      <td>Description</td>
      <td>2018</td>
    </tr>
    <tr>
      <td>Bar</td>
      <td>Description</td>
      <td>2017</td>
    </tr>
    <tr>
      <td>Bar</td>
      <td>Description</td>
      <td>2017</td>
    </tr>
    <tr>
      <td>Bar</td>
      <td>Description</td>
      <td>2017</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Foo</a></td>
      <td>Description</td>
      <td>2019</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Foo</a></td>
      <td>Description</td>
      <td>2019</td>
    </tr>
    <tr>
      <td><a href="https://example.org">Test</a></td>
      <td>Description</td>
      <td>2018</td>
    </tr>
  </tbody>
</table>

Sadly doesn't work like expected. It doesn't leave in the table only the elements that contains one of those two strings. I've checked the positions saved in the array and is all correct.

8
  • Can you include HTML at the question? Commented Feb 3, 2019 at 2:49
  • Sure, one moment. Commented Feb 3, 2019 at 2:50
  • Is this what you are attempting? Pseudo: if STRING does contain X AND STRING does contain Y - delete. It's that simple right? Commented Feb 3, 2019 at 2:52
  • @RandyCasburn If String does NOT contain X or Y - delete. Commented Feb 3, 2019 at 2:57
  • You want to remove the strings not the elements, correct? Commented Feb 3, 2019 at 2:57

2 Answers 2

1

.push() the element to posToRemove instead of i and substitute posToRemove[i].remove() for trTags[i].remove(). Also the first character of each string should be uppercase instead of lowercase to match "Foo" and "Bar".

var posToRemove = [];
var tbody = document.getElementsByTagName("tbody")[0];
var trTags = tbody.getElementsByTagName("tr");
var substring0 = "Foo";
var substring1 = "Bar";

for (let i = 0; i < trTags.length; i++) {
  var trTextContent = trTags[i].textContent;
  if (trTextContent.indexOf(substring0) !== -1 || trTextContent.indexOf(substring1) !== -1) {
    //do something
  } else {
    posToRemove.push(trTags[i]);
  }
}

for (let i = 0; i < posToRemove.length; i++) {
  posToRemove[i].remove();
}
<table>
<tbody>
<tr>
  <td><a href="https://example.org">Foo</a></td>
  <td>Description</td>
  <td>2019</td>
</tr>
<tr>
  <td><a href="https://example.org">Test</a></td>
  <td>Description</td>
  <td>2018</td>
</tr>
<tr>
  <td>Bar</td>
  <td>Description</td>
  <td>2017</td>
</tr>
</tbody>
</table>

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks! Can I ask you why your solution works while mine didn't?
@JessicaJ 1) The case of the first letter of the strings to match is not the same. 2) To reference the tr element in last for loop trTags[posToRemove[i]].remove() is necessary (see bracket notaation), as only an index is pushed to posToRemove at posToRemove.push(i) at first for loop, not the element.
I tried with trTags[posToRemove[i]].remove() but it doesn't work either.
@JessicaJ Not sure what you mean by "doesn't work either". If you use posToRemove.push(i) then use trTags[posToRemove[i]].remove() else use the code at the answer. Are you trying to match "foo" and "bar" or "Foo" and "Bar"? The code at the answer achieves the expected output, correct?
Yes, correct, because you pushed the element instead of i. So at the end you iterate on the elements and remove them directly. But If I save i instead of the element and then use trTags[posToRemove[i]].remove() it doesn't give me the expected output.
|
1

row.textContent.search(RegEx)

  1. Collect all of table with .rows HTMLCollection and convert it into an array.

    const T = document.querySelector('table');
    const R = Array.from(T.rows);
    
  2. Run .forEach() on the array and at the beginning of each iteration of a row, get the .textContent.

    R.forEach((row, idx) => {
      const str = row.textContent;
      ...
    
  3. Next make a regex to use with .search() method. search the string from the previous step. The regex is a simple literal search for Foo alternation: | to Bar. The result of the search will be the index of the found substring or -1 if there was nothing found.

    const rgx = /Foo|Bar/;
    const i = str.search(rgx); 
    ...
    
  4. If the result is -1 we know that this row should be removed and we do so using a .parentElement property and .removeChild() method.

    if (i === -1) {
     row.parentElement.removeChild(row);
    }...
    

Demo

const T = document.querySelector('table');
const R = Array.from(T.rows);

R.forEach((row, idx) => {
  const str = row.textContent;
  const rgx = /Foo|Bar/;
  const i = str.search(rgx);
  if (i === -1) {
    row.parentElement.removeChild(row);
  }
});
<table>
  <tbody>
  <tr>
    <td><a href="https://example.org">Foo</a></td>
    <td>Description</td>
    <td>2019</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Test</a></td>
    <td>Description</td>
    <td>2018</td>
  </tr>
  <tr>
    <td>Bar</td>
    <td>Description</td>
    <td>2017</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Foo</a></td>
    <td>Description</td>
    <td>2019</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Test</a></td>
    <td>Description</td>
    <td>2018</td>
  </tr>
  <tr>
    <td>Bar</td>
    <td>Description</td>
    <td>2017</td>
  </tr>
  <tr>
    <td>Bar</td>
    <td>Description</td>
    <td>2017</td>
  </tr>
  <tr>
    <td>Bar</td>
    <td>Description</td>
    <td>2017</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Foo</a></td>
    <td>Description</td>
    <td>2019</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Foo</a></td>
    <td>Description</td>
    <td>2019</td>
  </tr>
  <tr>
    <td><a href="https://example.org">Test</a></td>
    <td>Description</td>
    <td>2018</td>
  </tr>
  </tbody>
</table>

8 Comments

Thank you for your answer! Would you be able to tell me why my solution doesn't give me the expected output? I've updated my question.
@JessicaJ .getElementsByTagName(); returns a Live Collection. A live collection is updated immediately so when used in a loop it's overall length is affected on immediately. When you remove a tr from the Live Collection, the overall count diminishes on each iteration. To avoid the side effects of a Live Collection, use querySelectorAll() -- it returns a NodeList that's static so when you loop through it the overall length will persist. Did my solution help? If so upvote if not at all and a waste of time, downvote.
@JessicaJ Also I'm doubtful about .remove() method since its interface is Node not Element the difference between them is that Element interface deals with tags (ex. <div>, <a>, <table>, etc.) while the Node interface picks up on tags, text, and Document. Document object doesn't apply to your situation, but I'm assuming text might. Note in my demo I use a method from the Elements interface .removeChild()
@JessicaJ if you are wondering why the accepted answer is successful whilst yours is not is this: posToRemove.push(trTags[i]); vs. this: posToRemove.push(i);. The former abandons the last loop and presents the remaining elements matching Foo and Bar criteria. Yours continues to the last loop to reference and remove non-matching tr. Although it's an extra step in the process which would achieve your objective, but the last loop will shrink progressively and not only finish earlier than expected -- it should look almost random since the index changes on each iteration as well.
Thank you so much for the explanation! I have upvoted your answer. By the way If I was going to use a counter to remember how many elements I have removed so far from the live collection would this help me to keep track of the overall lenght and therefore know how the indexes have changed after each removal?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.