I'm asking for single paged JavaScript applications where the page does not make a full request. Specifically for a site like YouTube where the initial request is a full request and everything else is a XMLHttpRequest where only the body is changing client side.
An example would be YouTube. I want to do something like this:
You go to YouTube.com and click on one of the videos.
When the video page finishes loading, console.log("page loaded");
Is there an event listener I can add for when the page loads?
No events are being fired when the DOM is modified, so it's not possible to add a listener on anything like that.
You will have to keep track of the modifications on the DOM, manually. You could add "callbacks" to the modification functions you use:
function addSomethingToPage(data){
data.doSomething():
domReady();
}
function domReady(){
//re-usable "callback"
}
Related
I'm currently working on a Chrome extension that modifies content on a user's Tumblr dashboard, which uses infinite scrolling. However whenever the use scrolls down, a function needs to run again.
Here's a basic run-down of how I've got it working right now:
User loads page
Extensions modifies elements on page
User scrolls down
Triggers infinite scrolling
Next page loads below current one
More content loads
After that final step, I need step 2 to trigger again and have the new content modified.
I've tried .binding elements such as the entire <body>, the container div around the elements, and to no avail.
How do I trigger a function so that it runs when the content of a page changes (specifically the Tumblr dashboard)?
jQuery is fine, by the way.
You should set up a MutationObserver in your content script to watch for insertions of elements you want to modify.
See this question for more details.
Also, the Mutation Summary library might work well in your case.
You can try jQuery.ajaxComplete. It runs whenever there is an ajax request completed. You could have something like
$( document ).ajaxComplete(function( event, xhr, settings ) {
if (settings.url === 'tumblr.com/update') { //obviously change the update url
//do your thing
}
});
Of course the best way would be to find the actual function that gets fired on the scroll and modify it to fire yours on its success. But give that a shot.
Since some time last year, YouTube made it so that every page is not actually loading an entirely new page, but primarily just re-loading the contents in div#content. You can notice this when you click on a link in YouTube and see the red loading bar at the top of the page.
I have a Greasemonkey script that modified elements on YouTube, but now that YouTube doesn't reload the entire page, the Greasemonkey script no longer fires on every "new" page. How can I make the Greasemonkey script fire on every "new" page that I load on YouTube?
I'm using jQuery in this Greasemonkey script. I tried using functions like .on() with DOMNodeInserted but I can't find the right combination to make it work properly. With the event listeners that I've been using, I end up running my script hundreds of times for each page load, such as with the following:
$('div#page').on('DOMNodeInserted', 'div#content', function() { });
Another solution I was thinking of was making all links on YouTube load pages like normal, without the new way that they are doing it.
I figured it out myself after some research. First off, I don't like solutions that use setTimeout. This is often one method suggested in favor over the deprecated DOMNodeInserted for instance (which I use in some of my scripts, but try to avoid as much as possible), but if possible, I always prefer a solution where the script actually executes after a specific event. I've posted the solution I initially used in the first section below, then the final solution I used in the second section. All code below requires jQuery.
Decent solution, but not the best
At first, I had a solution where I added a click event to all A elements, which would run a timer that ran my script after 2 seconds. This isn't elegant, because if the page loads quickly, then there's a split second where the script hasn't run. And if the page loads for more than two seconds, then the script doesn't run at all. Script below:
$('a').click(function()
{
setTimeout(youtubeFunction, 2000);
});
Much better solution
So I began looking for a solution that was related to what I wanted to accomplish. I eventually found other people with a similar problem to mine (such as people wanting to create a Chrome script that modifies YouTube pages). This led me to this particular Stack Overflow solution, which basically says that the red loading bar at the top of YouTube pages was a CSS transition element, and that it created a transitionend (case sensitive) event when it was finished. The code in the linked solution wasn't complete (for me anyway), but it did explain how to achieve a working solution. The code I have runs only once per page, which is perfect. So here's what I have now:
function youtubePageChange()
{
youtubeFunction();
$('body').on('transitionend', function(event)
{
if (event.target.id != 'progress') return false;
youtubeFunction();
});
}
$(youtubePageChange);
To explain the code above, basically I run the code once for when you first load a YouTube page (such as by typing the URL in the address bar). Then for every subsequent click that requires the progress bar, the code runs again.
Red progress bar code
Oh, and for future reference, when the red progress bar appears at the top of YouTube pages, the site temporarily adds a new DIV to the end of BODY, with the following code:
<div id="progress" class="waiting" style="transition-duration: 400ms; width: 99%;"><dt></dt><dd></dd></div>
You can set a listener which gets called when the page has finished loading.
This is for the new YouTube material design:
body.addEventListener("yt-navigate-finish", function() {
//your code
});
And this for the old:
window.addEventListener("spfdone", function() {
//your code
});
(if you are using *monkey, you'll need to use unsafeWindow)
Keep in mind that the old design will be discontinued, so your script may not work after that.
Hooking into the popstate might be an option, but i was unable to make that work correctly for some reason (youtube may be preventing it from propagating), so i came up with this that shows the concept:
var currentState = "";
setInterval(function(){
if (currentState != history.state["spf-referer"]) {
currentState = history.state["spf-referer"];
console.log("Do Stuff!");
}
},250)
Just watches for the history.state to change, at which point it will log. The state should change any time the url changes, even if it wasn't due to a page reload.
I'm building a single-page-application which (pre-)loads the content of different pages via ajax. when the user navigates, the app replaces the old content inside a specific tag with the new content (ajax data). this new content could be e.g. an article containing text and images.
my question is: is there an event that is fired, after replacing the content and every external resource of the new content is loaded? like a "dom ready" for a refresh of the DOM?
Yes, jQuery has a document.ready event that should work for you.
You can however replicate jQuery's event, if you don't want to import the library.
Edit
Turns out I wasn't paying attention.
No events are being fired when the DOM is modified, so it's not possible to add a listener on anything like that.
You will have to keep track of the modifications on the DOM, manually. You could add "callbacks" to the modification functions you use:
function addSomethingToPage(data){
data.doSomething():
domReady();
}
function domReady(){
//re-usable "callback"
}
I'm writing a small website which has several pages that are very similar. Most of the time, only the content of one div is different. The navigation, header etc stays the same.
So I realized this with a "base" html file, some smaller html-files with only a content-div and javascript code like this (which is triggered by a button click event):
$.get("content/text1.html", function(data) {
$("#content").html(data);
});
This works very smooth but the problem was, that the url in the address-bar doesn't change with those kind of requests. So it is not possible for the user to link to certain pages. I know it is possible with #-urls, but i want to have urls like:
example.com/talks/foo/bar
And not some workaround.
In another Thread, someone gave me a hint to the html5 browser history api (especially history.js).
What I'm trying to achieve with it:
Someone clicks on a button -> an ajax request is triggered and the content of the content-div gets updated -> the url gets updated to something like example.com/talks/foo/bar
If someone requests example.com/talks/foo/bar in his browser directly, the same ajax request and content update as in (1) should be performed
I tried to realize the first one with:
$.get("content/text1.html", function(data) {
$("#content").html(data);
History.pushState(null, null, "content/text1.html");
});
But how am I supposed to achieve the second point? With a rewriterule, that redirects everything to the base-html file and some js-logic in it to decode the url and trigger the ajax request?
I have the feeling, that I am a bit on the wrong path..
So is this the way history.js should be used?
How can i achieve the second bullet point?
To get the initial state in html5 browsers no ajax calls are required. Like you said the url itself gets changed, not the hash so the server should reply to the url with the correct content already loaded.
You should do all your ajax calls and DOM manipulation inside the statechange event handler.
So when the user clicks on a link all you do is call pushState and handler the DOM changes in the statechange event handler. This works because statechange is triggered when pushState is called.
I am not sure if this is possible but I have a small web application that is responsible for uploading files to the server. This app is used by another app which includes it in an iFrame.
The uploader app is using jQuery form to submit the file in the background and I would like to be able to notify the parent app when the upload is complete and the success callback fires.
I was thinking about somehow passing a function into the iFrame app that could be called from the parent but I am not sure how to do this or if it is even possible.
If both apps are hosted on the same domain, accomplishing this is trivial.
To call a function in the parent window from the iframe:
parent.myFunction();
To call a function in the iframe from the parent window:
document.myFrameName.myFunction();
(This requires that the iframe have a name attribute set).
If the apps are on different domains, you'll have to perform some greater hackery to accomplish this.
You can call parent.functionname. Or you can try the IFrame event, onLoad to call from the parent page itself.
I'm not sure what's the right way to do this, but you can write something like document.callback = function() { ... } in your parent frame and parent.document.callback() in your iframe.
Of course your pages in parent frame/iframe must be from the same domain or browser will block any interaction between them.