19

Let's say I have an extension that loads when you arrive at a YouTube video page.I have noticed that when one navigates back and forth using the Chrome buttons, the extension most probably won't load.

As an example, I have 2 files, the manifest:

{
    "name": "back forth",
    "version": "0.1",
    "manifest_version": 2,
    "description": "back forth",
    "permissions": ["storage", "*://www.youtube.com/watch*"],
    "content_scripts": [
        {
            "matches": ["*://www.youtube.com/watch*"],
            "js": ["contentscript.js"]
        }
    ]
}

and the contentscript

alert("loaded");

The alert does not always show up when navigating back and forth. How can I overcome this, so that the extension loads every time?

Rob W
  • 341,306
  • 83
  • 791
  • 678
abinop
  • 3,153
  • 5
  • 32
  • 46

1 Answers1

27

YouTube has started a trial with pushState-based navigation. In general, such navigations can only be detected within content scripts by injecting code that intercept calls to history.replaceState / history.pushState (or by using the chrome.webNavigation.onHistoryStateUpdated event in the background page).

The remainder of this answer is specific to YouTube.
YouTube shows a (red) progress bar on top of the page during load. This progress bar is animated using a CSS transition. Since transition events bubble, you can bind a transition event listener to <body>, and check navigation in these cases.

You have to insert your content script at *://www.youtube.com/* instead of *://www.youtube.com/watch*, because pushState can be used to navigate from / to /watch...

function afterNavigate() {
    if ('/watch' === location.pathname) {
        alert('Watch page!');
    }
}
(document.body || document.documentElement).addEventListener('transitionend',
  function(/*TransitionEvent*/ event) {
    if (event.propertyName === 'width' && event.target.id === 'progress') {
        afterNavigate();
    }
}, true);
// After page load
afterNavigate();

Note: This method depends on the fact that the progress bar is inserted. Whenever Google decides to rename the ID of the progress bar, or remove the progress bar altogether, your code will cease to work.

Note 2: This only works for active tabs. If you need to detect navigation changes while the tab is not focused, then you need to bind a window.onfocus and window.onblur event, and check whether document.title has changed between these events.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Thanks for this answer. Using this method it works but it's not as early as DOMContentLoaded, it seems this is equivalent to `load`, is there anyway to detect the `DOMContentLoaded`-like point? – Noitidart Nov 17 '14 at 17:35
  • 1
    @Noitidart If you really want to get notified as soon as possible, you could bind to the `transitionstart` event, poll for document changes (using `setTimeout` or `setInterval`). When you've found the desired element, unregister the transitionstart event + stop polling + invoke your custom callback. To make sure that you don't waste CPU cycles, combine this with the transitionend event; if your polling logic has not detected anything at that point, remove the event listeners, stop the poller and invoke the callback. – Rob W Nov 17 '14 at 17:42
  • Genius idea @RobW thanks! I tried adding `window.onpopstate = function(e){ console.log(e) }` i thought that would do the trick but im not getting any callbacks to it. – Noitidart Nov 17 '14 at 17:51
  • `location.path` is `undefined`, so I ended up using `location.pathname`. Any comments on that? Besides that, it works perfectly – sports Mar 30 '15 at 20:49
  • 2
    @sports `location.path` is a typo, it should be `location.pathname`. – Rob W Mar 30 '15 at 20:50
  • Sheesh Rob man you get some easy stack points :P nice ;) For firefox though wouldnt nsIWebProgressListener give us a fool proof way to watch youtube page loads? – Noitidart Sep 27 '15 at 18:24
  • 1
    @Noitidart yes, [`nsIWebProgressListener`](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWebProgressListener) would probably the Firefox equivalent of `chrome.webNavigation.onHistoryStateUpdated` (but only in the main/background script, not in content scripts, just like Chrome). – Rob W Sep 28 '15 at 14:44
  • is this solution still valid in 2021? I tried and it only worked on browser refresh. when I clicked on a different video shown in youtube recommendations, the script did not alert the message. I'm wondering I missed something or this solution is no longer valid. – srini Dec 22 '21 at 00:28
  • I found another post that helped me in 2021: https://stackoverflow.com/questions/34077641/how-to-detect-page-navigation-on-youtube-and-modify-its-appearance-seamlessly – srini Dec 22 '21 at 11:36