Remove or hide specific table rows

Subscribe to Remove or hide specific table rows 16 posts, 4 voices

 
Gingerbread Man User

Hello,

I would really appreciate it if someone could *please* write or point
me to a script that can remove or hide an element based on the
attributes of a descendant tag. Here's an example of the situation:

<tr>
<td><img src="red.gif"></td>
<td>Miscellaneous text</td>
</tr>

The src attribute of the descendant image is the only thing that sets
these table rows apart from the other rows; that means I can't hide it
with CSS. Unfortunately I don't know any Javascript and Platypus has
been no help at all.
If you're not that inclined to help me specifically, I'd like to point out
that such a script has potential to be a versatile customizable
content remover: for example, most forums posts have an avatar
inside a table cell—this could double as a user ignore script.

I've looked around for a suitable script for a really long time, and I've
only been able to find two that more or less fit the description, but
they don't work.

This one hides the entire page (!) even after I set keywords that
aren't on the page
http://userscripts.org/scripts/show/6562

This one simply does nothing. In Greasemonkey I set it to run on
all pages and I added red.gif to the ignoreList array... but then
again I'm not sure what I'm doing.
http://userscripts.org/scripts/show/6288

 
znerp Scriptwright

You could just find such images and then remove their parentNode. Something like the following...

images = document.evaluate('//tr/td/img[@src="red.gif"]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null)
for (i = 0; i < images.snapshotLength; i++) {
  (foo = images.snapshotItem(i).parentNode.parentNode).parentNode.removeChild(foo)
}

 
Gingerbread Man User

That is brilliant! Another couple of questions if you don't mind:

1. What would be the code to remove the grandparent element of a link instead of an image (basically, <a> instead of <img> in the above example)?
After a lot of looking around, it seems that //tr/td/img[@src="red.gif"] should be //tr/td/a[@href="http://www.example.com/red.gif"] but that alone obviously doesn't get me anywhere.

2. How would you handle multiple keywords? Say you wanted to get rid of the table row based on red.gif as well as crimson.gif. I simply copied and pasted the code and made the change; that seems to work OK. Is there a better way to go about it?

Thank you very much!

 
znerp Scriptwright

1. What would be the code to remove the grandparent element of a link instead of an image (basically, <a> instead of <img> in the above example)?
Yeah, I believe that what you said should work.
2. How would you handle multiple keywords? Say you wanted to get rid of the table row based on red.gif as well as crimson.gif. I simply copied and pasted the code and made the change; that seems to work OK. Is there a better way to go about it?
This is surprisingly easy... '//tr/td/img[@src="red.gif" or @src="crimson.gif"]'.

So to do both the link and the images, you would want something like...

'//tr/td/img[@src="red.gif" or @src="crimson.gif"]|//tr/td/a[@href="http://www.example.com/"]'
There's probably a better way of doing this though, so if anyone wants to correct me, feel free..

 
Gingerbread Man User

Thanks for the reply!

After an enormous amount of trial and error, it finally dawned on me that it wasn't working because one particular page I was trying to apply it to had another element between the table cell and the image or link. I adjusted it accordingly and it seems to work like a charm now!

Would it be possible to match based on the inner markup of an element and to remove its parent as well as the following element (non-sibling)?

<tr title="This row would be removed">
<td>Miscellaneous text</td>
</tr>
<tr title="This row would be removed as well">
<td>Other text</td>
</tr>
'//tr/td[contains(text(), "Miscellaneous")]' is as far as I got; I saw references to followingNode here and there, but I have no idea how to piece it all together.

 
znerp Scriptwright

To match based on the text, you want to use this... '//tr/td[text()="Miscellaneous text"]' (EDIT: jsut saw the end of your post .. that would work, and might well be a better method depending on the situation.)

To remove the next element, just use nextSibling. So to remove both, the code I gave you earlier becomes..

for (i = 0; i < images.snapshotLength; i++) {
  (foo = images.snapshotItem(i).parentNode.parentNode).parentNode.removeChild(foo.nextSibling)
  foo.parentNode.removeChild(foo)
}
It would be equivalent, but to ease readability though you might want to make it as follows...
for (i = 0; i < images.snapshotLength; i++) {
  foo = images.snapshotItem(i).parentNode.parentNode
  foo.parentNode.removeChild(foo.nextSibling)
  foo.parentNode.removeChild(foo)
}
It is important here to remove the next sibling before the original node, because if you remove the original one first you cannot then reference it to find the next one.

 
Gingerbread Man User

Thanks again for the reply, but I can't get this one to work. Did I get it wrong somewhere?

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Table</title>
</head>
<body>
<table>
	<tr><td>Miscellaneous text</td></tr>
	<tr><td>Other text</td></tr>
</table>
</body>
</html>
images = document.evaluate('//tr/td[text()="Miscellaneous text"]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null)
for (i = 0; i < images.snapshotLength; i++) {
  foo = images.snapshotItem(i).parentNode.parentNode
  foo.parentNode.removeChild(foo.nextSibling)
  foo.parentNode.removeChild(foo)
}

 
znerp Scriptwright

I can't see something obviously wrong with that code, so try sticking alert(images.snapshotLength) between the first and second lines. If you get a messagebox with zero in it then the first line's failing, if it's > 0 then it's the code below that's wrong.

 
Gingerbread Man User

The alert displays 1
The two rows remain visible.

 
lambroger Scriptwright

try this
change

foo = images.snapshotItem(i).parentNode.parentNode

to
var foo = images.snapshotItem(i).parentNode.parentNode

 
Gingerbread Man User

Thank you, but it still doesn't work. The Error Console displays the following message. The line referenced points to foo.parentNode.removeChild(foo.nextSibling)

Error: Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMHTMLTableElement.removeChild]

I tried disabling every extension except Greasemonkey just in case, but it didn't help.

 
znerp Scriptwright

Try replacing that line with...

if (foo.nextSibling) foo.parentNode.removeChild(foo.nextSibling)
It sounds to me like you're trying to remove a node that doesn't exist (foo.nextSibling), so maybe there is no sibling after the returned result. I would guess though that this will remove the one row with "Miscellanous stuff" but not the "This row will be removed as well" row.

 
Osias User

the nextSibling returns null when you're at the end of the table

 
Gingerbread Man User

Try replacing that line with...

Now it gets rid of the whole table instead of the two rows (to test, I inserted a third single-cell row with random text, but everything else is the same as in the previous post). Nothing in the Error Console this time.
If I comment out foo.parentNode.removeChild(foo) then nothing gets removed.
It sounds to me like you're trying to remove a node that doesn't exist (foo.nextSibling), so maybe there is no sibling after the returned result.

The returned result? You mean the TD itself? In that case, there isn't. That's visible in the test case page above. I would like the script to:
- Match TD that contains text
- Remove next non-sibling element after TD, or next sibling of its parent element (same thing)
- Remove its parent element

I'm sorry, I hope this isn't too much trouble.

 
znerp Scriptwright

Now it gets rid of the whole table instead of the two rows
I read this line and realised I'm being stupid.

Before, we were grabbing an element in the row, and now we're grabbing the row instead of an element in it, but I didn't change how many times we go up a node. That is, we only need to go up once to get the row we want to remove, and not twice.

Change the line

foo = images.snapshotItem(i).parentNode.parentNode
to
foo = images.snapshotItem(i).parentNode

Sorry about that, I don't know why I didn't spot that one.

 
Gingerbread Man User

Change the line

Now it only removes the first TR (whose child TD matches "Miscellaneous text").

Wouldn't it be simpler to match the elements with XPath and then just remove them? There wouldn't be a need to know how many nodes to go up or down and so on. The following makes the alert display 2 but still only removes the first TR.

images = document.evaluate('//tr[td[text()="Miscellaneous text"]]/following-sibling::node()[position()=1]|//tr[td[text()="Miscellaneous text"]]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null)
alert(images.snapshotLength)
for (i = 0; i < images.snapshotLength; i++) {
foo = images.snapshotItem(i)
foo.parentNode.removeChild(foo)
}
Edit
After a maddening amount of trial and error, I managed to get it to work by duplicating it as follows: first try to remove the sibling of the parent TR, then both the parent TR and the sibling TR. As far as I can tell, nothing else works and I have no idea why.
images = document.evaluate('//tr[td[text()="Miscellaneous text"]]/following-sibling::node()[position()=1]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null)
for (i = 0; i < images.snapshotLength; i++) {
foo = images.snapshotItem(i)
foo.parentNode.removeChild(foo)
}

images = document.evaluate('//tr[td[text()="Miscellaneous text"]]|//tr[td[text()="Miscellaneous text"]]/following-sibling::node()[position()=1]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null)
for (i = 0; i < images.snapshotLength; i++) {
foo = images.snapshotItem(i)
foo.parentNode.removeChild(foo)
}

If you have any other tips, I'm all ears, if not, then thank you very much for your invaluable help. I really appreciate it.