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.
Related
I have a web page which is dynamically built by the client. It generates dozens of list items each with its own named anchor. The side of the page has a fixed table of contents (TOC) with hyperlinks that point to the named anchors. This allows the user to click a TOC entry a jump to the item.
The trouble I am encountering is that on the initial page load the page is dynamically generated and so it cannot scroll to the item in the initial hash of the URL using the default behavior of a browser. Additionally, when the user switches to a different book the page is completely regenerated with new content and a new starting hash. Same problem: since the hash preexists the content, it doesn't situate itself with the item already in view.
I nearly solved this with JavaScript by awaiting the rendering and then jumping to the hash using scrollIntoView method on the appropriate element.
The next problem is that the stylesheet is not fully applied by the time scrollIntoView is called and so the final position of the item is unknown. I see the unstyled item scroll into view, but once styling is applied the positioning is lost. I put a 1 second setTimeout in place to delay the scrollIntoView call. This works but feels fragile.
Are there reliable techniques for jumping to a named anchor when the content comes after the hash is in place? If I knew when the CSS was done styling content that might help. Alternately, it might be useful to trigger an event once the height of the page stabilizes (thus signaling the finalization of CSS styling).
I had a similar problem, although in my case only the table of contents and named anchors were autogenerated in the onload handler - not the rest of the page content. I solved the initial hash problem by adding the following code to my onload handler after generating the anchors:
if (location.hash)
{
var requested_hash = location.hash.slice(1);
location.hash = '';
location.hash = requested_hash;
}
I had to set the hash to '' before setting it back to the requested name to make the browser respond. This seemed to work in all the browsers I tried (Opera, Chrome, Edge, IE, FF).
You can use jQuery if you will always know the name of the element you want to set focus to. You can run this after your page has loaded:
$( "#targetElementGoesHere" ).focus();
Edit: To scroll to that, check out https://github.com/flesler/jquery.scrollTo
I think the answer you require was answered by this guy...
How to wait until a web page is loaded with javascript?
So, something like this...
document.onload = function(){
scrollIntoView...
}
I don't know if this is the effects of an update panel or what, but I basically have a drop down list that allows a user to select an item as a filter. When the item is selected it should bring back only one item into a grid view. That is this specific filter will at most bring back the record you are looking for. This works fine if the user clicks an "apply" link to apply the filter. Behind the apply link is some server-side code (C# within an ASP.NET Web Forms application).
We had a request by a user with something to the effect of:
"Why do I have to click the apply button if I make a selection in this
one drop down filter...it should simply get that one record I am
searching for. This helps me because I don't have to click the
"Apply" button."
I agreed with him and thought what is the easiest way to do this...I thought: Simple, I will have an on change event handler of the drop down such that when a selection is made I'll trigger a click event. Something to this effect:
$("#MainContent_ddlCompany").on("change", function() {
var companyId = $("#MainContent_ddlCompany").val();
$("#MainContent_hdnCompanyValue").val(companyId);
$("#<%=ddlCompany.ClientID %>").trigger("chosen:updated");
if (companyId.length > 0) {
$(".apply").click();
$(".apply").removeClass("applyButton");
$(".apply").addClass("resetButton");
} else {
//cleared selection of a company
$(".apply").removeClass("resetButton");
$(".apply").addClass("applyButton");
}
});
At first this didn't work, and I couldn't tell why, but then after some serious googling I changed this line:
$(".apply").click();
To this:
$('.apply')[0].click();
That worked great...so I decided to test it some more. As I kept selecting one filter value after another I noticed the page started to slow down. In fact by the 6th or 7th time it was pretty unusable. I don't know why it's happening, but I suspect again it has to do with the fact that this linkbutton with the class name .apply is inside an update panel.
But still I thought to myself, it was inside of an update panel before I changed my jQuery code to simulate the click event. So why does the page slow down and drag with this little piece of code? Is calling the event from jQuery code rendering something else in the HTML that could be causing this?
If I change my code back and force the user to click the apply button then we are back to a good normal speed. Why is it if I tell jQuery to simulate clicking the button my page slow down? It's doing the same thing, the simulation of the click of this link button is calling its server-side code method whether the user clicks it or I have jQuery click it.
For now I'm at a loss as to why this is happening because this button is in an update panel in either case, yet when I have jQuery click it via $('.apply')[0].click(); the page slows down after several attempts. Yet when I have the user simply click this button (without the jQuery click event) then it works fine?
What am I missing here?
Ugh, well, I found my issue. Because I was using updatepanels I had to wrap my jQuery code to include an add_endRequest. That is, you have something to the effect of:
$(document).ready(function() {
//Some initial event/triggers
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function () {
//Copy of some initial event/triggers
});
});
Why do I use the endRequest you ask? Well, because updatepanels basically throw away all your events after an asynchronous postback because the HTML at that point (after an update) is rendered again and at that point all events associated with any control inside an update panel are wiped away. At this point of course document.ready() does not run, so I have to resubscribe to these events inside of endRequest. Enter my issue...
I had a huge brain fart where I basically took everything, literally everything inside document ready and copied it into endRequest. In fact, if I remember correctly, I read articles which stated
Whatever you have in document ready simply copy paste into endRequest
That's fine, but you have to be careful here. I was throwing in events that were not wrapped around inside of an updatepanel into endRequest. The result is disastrous...at least for me.
These events would be attached then multiple times..or based on the number of asynchronous postbacks made. In my case, as I was testing I mentioned after the 6th or 7th time performance starts degrading. Well, by that time my controls were being attached that many times to events. For instance, my .apply button along with my dropdownlist were both outside of my updatepanel. But my jQuery code was attaching the change event of my dropdownlist in both document ready and endRequest.
The result is initially it's pretty fast, because it's only in document ready. But as I make asynchronous postbacks these events are being attached every time. For n tests I would have n attached events...in my case the test of 7 yields 7 on change event handlers!
Case in point, do not place any event handlers such as jQuery's on() event for any controls that are NOT inside an update panel. Otherwise you will run into what I ran into which was poor performance as events are happening.
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 have an iframe tag and I want to dynamically change it using jquery animation. So for example the iframe sits on the home page, and if i click the about link, it will load the about.html and when its ready it will slide it down using animation.
I have the basic logic for it but then came about this
problem:
When I refresh the page it loads back the content of the index.html page, and what I want is that when I refresh it, it still keeps the contents of about.html.
About
<iframe id="content" name="content" align="top" src="index.html"
frameborder="0" width="100%" height="1200px" scrolling="no">
</iframe>
this is just the most basic logic, but I need help on how do I achieve the refreshing part/
and what if i dont include them in the same page but I still want to animate the page transitions. so when the users clicks a link to a new page, it will load it, and then animate it.How can I achieve this. Because recently I saw a jquery plugin callen LocalScroll and they achieve this effect, but i couldnt get it to work for new pages
Your reference to the jQuery plugin LocalScroll is on the right track. In fact, if you could implement it properly I think it would solve your problem.
Anchor-based navigation, as used in this plugin, jQuery Mobile, and other places, will update the window.location object and also be reflected in the browser's address bar so that, when an explicit page refresh occurs, the hashed location is preserved.
The answer, then, is to have a script which can parse this local link from the address. Here's a generic JavaScript code block to demonstrate this:
window.onload=function() {
var URLParts=window.location.toString().split('#');
if(URLParts.length>1)
var lastPage=decodeURI(URLParts[1]);
else
return false;
if(lastPage)
iframe_load(lastPage,'content');
}
function clear_last_page(location) {
var URLParts=location.split('#');
if(URLParts.length<=1)
return location;
URLParts.pop();
return URLParts.join('#');
}
function iframe_load(url,targetID) {
document.getElementById(targetID).src=url;
var location=clear_last_page(window.location.toString())+'#'+url;
window.location.href=location;
}
How it Works
When the window onLoad event is triggered, the URL is searched for anchor (hashed) links. If found, we will assume that this is a reference to a page and so then pass it to iframe_load().
This function does two things. First, it points your target inline frame to the page passed via url parameter. Second, it points the parent frame to a fictitious anchor, which will be preserved even after the page is refreshed.
Therefore, when you refresh the parent frame, that anchor text is grabbed, parsed, and used to re-load the last loaded inline page.
The function clear_last_page() is simply a helper function that prevents additional anchor links from being appended to the URL.
Demonstration
Visit this URL:
http://gocontactform.com/stackoverflow/dynamically-change-iframes-content/
Click the link "Page 2" to see the change. Then refresh the page.
Noteworthy
Be advised that this solution technically takes over the normal function of anchoring. So if you attempt to use anchor links normally on the page, you may get undesirable results.
You are forced to rely on iframe_load() for any links bound for that inline frame, instead of what you modeled in your question (traditional linking with a target attribute).
I might also suggest that you define no default src attribute inline. Rather, you could add to the onLoad handler a call to iframe_load('page1.html','content') and that will prevent the unnecessary attempt to load the default page when you are refreshing with anchored links in the address.
There are also other ways to accomplish what you are asking. But I believe that this solution is easy to understand and implement.
Hope that helps!
You can use the following to change the src attribute of the iFrame:
$("#content").attr('src', 'http://mysite.com/newpage.html');
Oops, looks like I misread the question.
If you want to slide it down, you can bind an event handler to the load event (jQuery doc) to do something when the frame loads.
$("#content").hide();
$("#loadLink").click(function() {
$("#content").hide();
$("#content").attr('src', 'http://mysite.com/newpage.html');
});
$("#content").load(function() {
$(this).slideDown();
});
In this example, the iframe is hidden when you click the link, and when it is ready, it slides down.
Demo
Edit: still misread it!
To save the state of which page is last shown in the iframe, you can use HTML5 localStorage.
In the load event of the iframe save the page that it's currently showing.
localStorage['lastPage'] = "about.html"
and then load it back using localStorage['lastPage'] on page load.
Updated demo showing both sliding and keeping the page after refresh.
Not possible. When you refresh a page, your browser is supposed to get the page from the server, dropping all JS data.
History API can help, but only for the newest browers.
Whenever the page loads you need to check something to know what the last src iframe loaded. By default, no browser can know this. One way to do this is to change the hash of your page when hit the click, and whenever page loads, you check if exists this hash and trigger some link with the hash.
I write this: http://jsfiddle.net/estevao_lucas/revsg/4/
Like said Michael, History API can help you.
I have a webpage that use $(document).ready() to build the interface. Then the user can go to a child page, and to go back to the original page he can press the browser's "previous" button or a "Return" button in the page which triggers a history.back();. Back on the original page, $(document).ready() is not triggered so the page is missing information.
Is there a way to trigger it automatically like if it was a "real load"?
edit
placing an alert in it, the alert is popped but stuff is missing in my interface like if some part of the ready event is missing. Investigating...
edit 2
hahahahaha in document.ready I click some checkbox which are supposed to be unchecked. When I "back" on this page, they are checked so they become unchecked because I reclick them.
Sorry, this is completely my bad :(
A quick solution to this problem, use "onpageshow" instead.
window.onpageshow = function(event) {
//do something
};
If the user uses the Back button to navigate and you require a full reload of the page, you can set the NO-CACHE policy of the page.
This way the browser is forced to reload the page from the server, even using the Back button.
1.) put scripts at the bottom of your page.
2.) execute plugins and whatnot in your last script tag(s).
3.) Do not use onDomReady implementations at all, it's redundant.
People are so accustomed to onload or ondomready, they overlook the fact that putting your scripts at the bottom of a page does virtually the same thing without the need to poll and see if your html is available.
Furthermore, it's also good practise as your scripts do not block html/css rendering either.
Not depending on onDomReady or onLoad implementations solves a lot of issues.
Very interesting question. You might need to re-trigger the event/function when the page gets focus, or something similar. you might also need to keep a flag variable to track whether an 'event re-triggering' is in order.