I am creating a chrome extension for gmail, and I have seen some odd instabilities around when certain page objects load. Since I am trying to modify the GMAIL DOM (Customize it for my extension) it needs to be fully loaded before my initializer runs. My extension always initializes well before the DOM is fully loaded, so I need a way to have it poll until the DOM is fully formed to do its thing.
I have tried using document.readyState to poll the document so I know when to start modifying the fully-loaded DOM, but it doesn't always work.
I put into the console a printout of document.readyState polled every 100 milliseconds once the page begins to load, and I noticed that about 1/10 page loads document.readyState becomes "complete" BEFORE the DOM is fully loaded. I can tell this because I do a jQuery selector for the GMAIL compose button and it comes up empty when this occurs, and find it as expected every other time (It finds the compose button).
Why would this be? Is there a better way to have my extension initialize script check to make sure the DOM is fully loaded before firing the DOM mods?
EDIT--
I have the script that checks document.readyState as a script injected into the UI as a content script.
Gmail website is dynamic. readyState / DOMContentLoaded and friends are all rather useless, since most of the UI is built on the fly and is not in the "initial" DOM. This is exactly what you're observing.
You need to either poll for the compose button as RobW mentioned, or watch for it being added with MutationObserver events. Also of note is the mutation-summary library.
Your code for document.readyState should be located on your content scripts.
The document referred by the background and popup pages is not the web page's.
More info here: https://developer.chrome.com/extensions/overview#arch
Related
Context:
I am creating a Chrome extension that hides certain elements of certain sites
In this specific case, I'm trying to hide the main feed of YouTube's home and trending pages
The script has no trouble on all other sites, including Twitter, Facebook etc.
But on YouTube, it's causing the page to crash
Roughly speaking, what the script does is:
Observes any mutation on document (childList: true, subtree: true, characterData: false)
Searches for the existence of certain nodes in the document
Changes some of their styles to hide them (or if already hidden, does nothing)
Adds a small menu into the node with a button to unhide the node
The MutationObserver is never disconnected because it needs to keep watching in the case of single-page apps where the page stays the same but different nodes come and go
So it keeps checking that the hidden nodes are still hidden every time there's any new mutation to the document or its subtree (heavy load on performance, I know - but it works fine on every other site)
YouTube issue:
YouTube always throws up a warning as follows, even when I am not running my script on it (in other words, YouTube's code is already a bit suspect):
[Violation] Added non-passive event listener to a scroll-blocking <some> event. Consider marking event handler as 'passive' to make the page more responsive. See <URL>
The specific event is either touchstart or wheel. This error can display in the 100s of times even when I am not running my script.
When I run my script, this error seems to blow up even more, and display more times than usual
Eventually, the entire page crashes or takes far longer to load than it should (but it does sometimes eventually make it all the way, showing that my extension is not completely breaking down)
There's also another warning that tends to show, [Violation] 'readystatechange' handler took <N>ms
This warning shows far fewer times than the other (see screenshot below)
Interestingly, usually loading youtube.com home page when starting off in a new tab is fine, and my extension successfully hides (i.e. changes styles on + injects some extra HTML into) the node it's meant to hide
I then get a crash or extremely slow page load when I try to navigate within YouTube, e.g. specifically going to the Trending page using the left-hand side menu, OR occasionally when I hit refresh on the page
Things I've tried:
Overriding the default addEventListener method on EventTarget.prototype, which I have so far failed to do successfully - not sure I understand how to do this despite trying a few methods from SO
Blocking the script that this error originates from (desktop_polymer_inlined_html_polymer_flags_v2.js) using the Chrome WebRequest API, but that doesn't work because it breaks the whole page
Questions:
Is it likely that this 'non-passive event listener' warning is interplaying with my script to cause the crashing of the page? Or that my script is causing more listeners to be added than the page would usually?
How can I stop this error from happening (e.g. how do I prevent the event listeners from being created by YouTube's JS)?
Does anyone know anything about the way YouTube is built that would make it crash if you try to 1) modify a style on an element directly 2) add another element into a parent element 3) continually check styles on an element? Builtwith.com was not much help.
Is there something else I am missing here? Another way I can change my content script to make it interplay better with YouTube?*
*I know a tempting answer will be 'don't observe the document'/'observe it less' but this is more or less non-negotiable in terms of the way the browser extension needs to work.
Screenshot:
Chrome profiling:
Note: having looked into them individually, none of the functions that are taking up the huge amount of time are part of my extension. So perhaps YouTube is reacting badly to the DOM modifications that my extension performs.
I'm trying to write a userscript working on the Etherpad page whose content will be updated continuously. The relevant part of the page structure is on the screen:
My userscript should affect all the div entries under 'td id="sidedivinner"' element. However, this element does not exist on the page initially and it is built only after the content of both iframes on the screen loads. All the solutions I found on the problem "Execute userscript after page load" failed, because they assumed either that there are no iframes, which still load after the script, or that iframe has the unique name or id.
So basically I want to execute js after all iframes load, as if I had done this via web browser console.
Also I want to run the same script after every AJAX request affecting sidedivinner (It will be too expensive to run it after every AJAX request). I suspect this solution won't work using #sidedivinner id because "waitForKeyElements.js" won't recognize an element by Id inside the iframe. How can I do this?
Add your javascript to the onload event of the iframe - not the page.
iFrame onload JavaScript event
You could use jquery to check for the creation of the #sidedivinner object. The following jquery will search in any iframes on the page.
$("iframe").contents().find("#sidedivinner")
See the following for more on these jquery functions:
https://api.jquery.com/contents/
https://api.jquery.com/find/
I just came across the window.stop()
The stop() method is exactly equivalent to clicking the stop button in the browser. Because of the order in which scripts are loaded, the stop() method cannot stop the document in which it is contained from loading, but it will stop the loading of large images, new windows, and other objects whose loading is deferred.
https://developer.mozilla.org/en-US/docs/Web/API/Window.stop
I am wondering:
What do 'windows' and 'other objects whose loading is deffered' refer to? Can somebody give me a practical example?
If I append an image to the body and call window.stop() when 30% has been loaded, and then append the same image at a later time; will the latter image only have 70% left load?
What do 'windows' and 'other objects whose loading is deffered' refer
to? Can somebody give me a practical example?
new windows could be iframes or regular frames. Scripts themselves can be marked as defer which loads them after the document and after other inline scripts so these could also be stopped with window.stop(). So practical examples of objects that might have their loading stopped are: iframes, embedded objects (like Adobe Flash), scripts marked defer and images. The idea here is that if you're going to call window.stop() from a script in the page, it can only apply to resources that have not yet completed loading. By definition, at least part of the document and your script have already completed loading or the script wouldn't be running so you could call window.stop() so it is primarily going to apply to resources that are loaded later in the load process such as the ones above.
If I append an image to the body and call window.stop() when 30% has
been loaded, and then append the same image at a later time; will the
latter image only have 70% left load?
The browser cache is very unlikely to cache 30% of an image and then resume loading only the remaining 70% the next time you ask for that resource. Most caches just refuse to cache an object if it is not completely loaded. In any case, this would be entirely browser dependent as this type of behavior of the cache is outside of the standards.
What do 'windows' and 'other objects whose loading is deffered' refer
to? Can somebody give me a practical example?
I use to do a practical to keep window calm when page reloading like this example.
window.stop();
(do some staff here..)
window.location.reload(true);
The window will automatically active by itself when page is reloaded.
So it stop only the browser, not the script.
I've got a piece of code that wants to perform a jump to a particular id on the page as soon as the page is ready. I accomplish this by performing a jquery.animate() so that the scrollTop is at my target element.
However, I'm using web fonts, and for some reason the ready event is firing before the web fonts have loaded and been applied. The result is that the animation ends on a position that is often completely unrelated to the actual position of the element I want to scroll to.
I've verified this by opening the timeline in the Chrome inspector, where I see the animation triggering, followed by the web font loading, followed by a re-render which causes my animation target scroll point to become meaningless. I've also confirmed that this issue does not manifest itself when I use a system font.
Could anyone offer some advice? Perhaps there's some sort of event that fires after a web font has been applied?
$(document).ready(...) is triggered when the browser has finished downloading the entire HTML of the page. It is often before the browser has finished downloading the stylesheets, let alone the font files.
Assuming it's loaded from a stylesheet included in the HTML (as opposed to a JavaScript added stylesheet), you should be listening for the window event, rather than the document's load event.
$(window).on('load', function(){
// your resources are loaded
});
Try using .load instead, as .ready is only after the DOM is loaded.
$(window).load(function () {
// run code
});
Here is info regarding why .ready() is NOT what you want:
http://api.jquery.com/ready/
Here is info why .load() (really the Javascript load event) is what you want (it waits for resources to be loaded):
http://api.jquery.com/load-event/
I'm trying to make the window.onload event fire sooner so that Google will think my page loads faster (this is a frustrating task since how long it takes to get to window.onload is basically irrelevant from the user perspective, but I digress)
However, I don't know what delays the onload event! Specifically:
If I load a Facebook likebox on my page in an <iframe>, does its loading delay the onload event? What about if the likebox iframe has to load a bunch of profile pics; does onload wait until they fully load?
Suppose that on document ready I do an async AJAX request for an HTML blob and inject it into the page. If this HTML blob contains a bunch of <img> tags, does the onload event wait for all of these to load?
In general, how does the browser know when to fire the onload event? What sorts of things block onload, and what sorts of things don't?
a) You can't control window.onload except by reducing the page "weight". Its up to the browser to decide when its going to declare that event.
b) Google doesn't have a clue about the window.onload event because its not parsing JavaScript.
1) You can completely eliminate the Facebook payload by using XFBML version of the like button and asynchronous loading of the Facebook JavaScript SDK (http://developers.facebook.com/docs/reference/javascript/FB.init/). Do note that it will work only if JavaScript is enabled.
2) Everything that is going to dramatically increase the weight of your web page should be loaded asynchrouniusly, preferably after the window.onload event has fired.
If you look at the waterfall, in firebug or chrome inspector, iframe and ajax calls does affect the onload event. I ran into similar problem with facebook considerably slowing down site. Yes, while looking at pageload time in webmaster tool, it shows the lag.
My solution was to dynamically append facebook iframe when the page is completely loaded. and for ajax calls, i only trigger them on load.
This brought my page load time from 7 seconds with embedded facebook iframe, to 2.6 seconds with dynamically appending it.