0

I'm trying to remove elements from webpages that aren't essential using querySelectorAll, but this doesn't seem to work on Opera Mobile 11.5. I've tried the scripts out on my PC before I've put them on my phone.

Mozilla's documents are confusing, because there are two pages about this function and one says it's compatible and the other says it isn't.

Is there a solution to fix this? Will I need to implement other libraries into these scripts I've made?

Here's how I'd like to do it:

function deleteByCSS(el) {
 elem = document.body.querySelectorAll(el);
 for (i=0; i<elem.length; i++){
  elem[i].parentNode.removeChild(elem[i]);
 }
}
deleteByCSS("here goes every CSS selector pointing to objects I want to remove");
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
user1263513
  • 91
  • 1
  • 8

3 Answers3

1

The problem is very likely that you are removing elements from the list as you are iterating them. A safe alternative is to loop backwards, that way, deleting nodes won't affect the rest of the list

function deleteByCSS(el) {
  var elem = document.body.querySelectorAll(el);
  for (var i=elem.length - 1; i>=0; i--){
    elem[i].parentNode.removeChild(elem[i]);
  }
}

By the way, don't forget to add var before your local variables

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Nope, this doesn't work either and I think looping backwards in this case is pointless, because the elem array is filled only once, so I can go through it in any way I wish to. – user1263513 Mar 12 '12 at 23:35
  • 1
    @user1263513: elem is a NodeList, which means it's live. Therefore, removing nodes from the DOM will also remove them from the list. If you wish to iterate forward and delete elements, you have to increment your loop index – Ruan Mendes Mar 12 '12 at 23:44
  • strange, I tried it on Firefox on my PC with complex CSS3 selectors resulting more than 10 elements collected and all of them got removed without a single error – user1263513 Mar 12 '12 at 23:54
  • 1
    @JuanMendes NodeList by querySelectorAll is not live, it's static. – Kernel James Mar 13 '12 at 05:11
  • @KernelJames Thanks, I wasn't aware of NodeLists that aren't live, but it's true https://developer.mozilla.org/En/DOM/Element.querySelectorAll – Ruan Mendes Mar 13 '12 at 17:23
1

Several issues:

  1. Juan Mendes' answer is right about the deletion order. If you don't delete in reverse, not only will the JS be inefficient, but there is the real chance of the script crashing due to array values that are no longer valid. If the inefficient way appears to work once or twice, that is just luck.

  2. Additionally, nested nodes need to be factored in, note the extra check in the code below.

  3. The fact that the code appears to work for you outside the Opera, User JavaScript, suggests that the nodes you are using are added by page javascript/AJAX. Compensate with a timer or similar method.

  4. If still encountering difficulties, Link to the target page and post the exact CSS selectors you are attempting.

  5. Note that querySelectorAll() is compatible with Opera Mobile 11.5, and both of those pages either say so or show a question mark -- which does not mean it is not compatible, just that Mozilla did not test it (others have). Note that there is potentially a difference between document.querySelectorAll() and {element}.querySelectorAll(), hence the two pages.

Putting it all together, code like this should work:

function deleteByCSS (cssSelector, delayLoopMax, delayMilliSecs) {
    //-- Use defaults, if not specified.
    var delayLoopMax    = delayLoopMax   || 0;
    var delayMilliSecs  = delayMilliSecs || 300;
    //console.log ("Args: ", cssSelector, delayLoopMax, delayMilliSecs);

    var doomedElements  = document.body.querySelectorAll (cssSelector);
    if (doomedElements  &&  doomedElements.length) {
        //console.log ("Found!");
        //-- Found the node(s); delete it/them.
        for (var J = doomedElements.length - 1;  J >= 0;  --J) {
            var doomedNode  = doomedElements[J];
            if (doomedNode) {
                doomedNode.parentNode.removeChild (doomedNode);
            }
        }
    }
    else {
        //-- Nodes not found. Wait for JS to add them, if specified.
        if (delayLoopMax > 0) {
            delayLoopMax--;
            setTimeout (
                deleteByCSS,
                delayMilliSecs,
                cssSelector, delayLoopMax, delayMilliSecs
            );
        }
    }
}


In this case, use it with an initial extra parameter of 20...

deleteByCSS ("*VALID* CSS selector", 20);
Community
  • 1
  • 1
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • This is really a good answer, but this function doesn't work either. I tried to wrap it and the calls with an anonynous function, then wrap it again with window.opera.addEventListener with AfterScript event specified, but still doesn't destroy elements. One of the websites is http://endlessvideo.com/ and the CSS selector is 'div[style="float: left;"] div[style="margin-left: 5px;"] div[style="float: right;"],div#addthis_button,div#uvTab,a[href*="vdownloader.com"],a[href*="youtubedownloader.com"]', which works perfectly on desktop Firefox – user1263513 Mar 13 '12 at 07:17
  • The code in your last comment is "messed up". Please edit the question to add any significant code. Don't use `addEventListener` or `AfterScript` here, that's what the timer code is for. In fact, this particular code would work fine running in [Greasemonkey mode](http://www.opera.com/docs/userjs/examples/#greasemonkey). ... Finally, I don't know if Opera Mobile supports multiple selectors in one go like that. Try something simple first, like `deleteByCSS ("#addthis_button", 20);`. I didn't see any iFrames, but be alert if some of these nodes are in one. – Brock Adams Mar 13 '12 at 07:35
  • I've tried now every combination of the functions provided in the answers in both UserJS and Greasemonkey mode. I highly doubt now that Opera Mobile really support querySelectorAll(). As an alternative, what do you think about this: http://v.gd/ovd9UR As you can see in the demo, it does exactly what the function in question does – user1263513 Mar 13 '12 at 07:58
  • If it works, go for it. Pretty sure that `querySelectorAll()` works though; will fire up the emulator, and double-check, when I feel frisky. ... **Try this first:** Replace `document.body.querySelectorAll` with just `document.querySelectorAll` and try a simple selector to start. – Brock Adams Mar 13 '12 at 11:02
  • OK, the only thing I haven't tried so far is my original snippet in Greasemonkey mode, where it really works. However, by the time it runs, a lot of things I'd like to avoid to be downloaded are already downloaded. Seems like its a limitation of Opera Mobile. I even wrapped it around window.opera.AddEventListener with BeforeScript and AfterScript, but the var elem gets populated only in Greasemonkey mode. Just wondering, will those contents be loaded if I inject a css to set display to none for them with a UserJS? – user1263513 Mar 13 '12 at 12:00
  • They will be loaded, but they will not be displayed. This is the same behavior as all the code in this question, so far. Nothing about the question pertained to stopping things *loading* -- only removing them from the *display*. So, injecting the CSS early, may work better for you than trying to delete the nodes. ... Also, If your original snippet worked in Greasemonkey mode, then both Juan Mendes' answer and my answer should work too. – Brock Adams Mar 13 '12 at 12:29
  • Sorry for not explain more clearly. I wanted to ask if the not displayed elements are still loaded into the RAM? Because, for example, on endlessvideo.com the sidebar fetches youtube video thumbnails that causes the browser to hang for a few moments. Not displaying the sidebar I guess will eliminate this issue. I wouldn't use the sidebar anyway. I guess I can close this now and choose the answer. Thanks – user1263513 Mar 13 '12 at 14:25
  • If the elements are just hidden (`display:none`), they will still be in the browser's memory and the page's DOM. If there is JS associated, it will continue to attempt to fetch thumbnails even if you can't see them. So, for that purpose, deleting the nodes is better. – Brock Adams Mar 13 '12 at 14:47
  • Good to know. Then I will just divide my scripts into UserJSs with CSS injection and Greasemonkey scripts the function I made for deleting nodes. – user1263513 Mar 13 '12 at 15:59
0

According to caniuse ( http://caniuse.com/#search=queryselector ) it should be supported by opera mini.

Anyway Use Zepto or jQuery for css dom selection if you want to avoid that kind of problems , or even just sizzle. querySelectorAll and querySelector are obviously not supported by all browsers, desktop or mobile. Mozilla's DOM api's reference is not very good or up-to-date in my opinion. Developpers really need a true complete and in depth DOM api documentation.

mpm
  • 20,148
  • 7
  • 50
  • 55
  • I guess you wanted to say Mobile instead of Mini. And I've tried to experiment with Sizzle before I ended up at the function I'm having problems with, but I just couldn't figure out how can I achieve the same results with Sizzle. The documentation on github and in the zip package shows me no examples I can learn its ways with. – user1263513 Mar 12 '12 at 23:31