I want to use javascript in the url bar to manipulate the rendered html of a given page. Please note that I'm not trying to do something illegal here. Long story short, my university generates a weekly schedule based on your courses. I'd like to use javascript to add a button on the generated schedule page that will allow you to push the schedule to a google calendar. Unfortunately, I can't just go and edit the source itself (obviously), so I figured I would use javascript to edit the page once it has been rendered by my browser. I'm having some trouble calling an external javascript file to parse the rendered html.
As it is, this is what I have:
javascript:{{var e=document.createElement('script');
e.src = http://www.url.of/external/js/file.js';
e.type='text/javascript';
document.getElementsByTagName('head')[0].appendChild(e);}
functionToCall(document.body.innerHTML);}
Which, when pasted into the URL bar, SHOULD add my javascript file to the head and then call my function.
Any help would be greatly appreciated, thanks!
EDIT: Here's a working example if you're interested, thanks everyone!
javascript:(function(){var e=document.createElement('script');
e.src = 'http://www.somewebsite.net/file.js';
e.type='text/javascript';e.onload =function(){functiontocall();};
document.getElementsByTagName('head')[0].appendChild(e);})();
If you need the code to execute once it is loaded you can do one of two things:
Execute functionToCall(document.body.innerHTML); at the bottom of your script (http://www.url.of/external/js/file.js) rather than at the end of your bookmarklet.
Use e.onload = function(){ functionToCall(document.body.innerHTML); }; after e.type='text/javascript' near the end of your JavaScript snippet / bookmarklet, rather than calling functionToCall right after appending e to the document head (since e will most likely not have been loaded and parsed right after appendChild(e) is called.
I see that you've accepted an answer, and that's perfectly valid and great, but I would like to provide a useful tool I made for myself.
It's a bookmarklet generator called zbooks.
(Yes it's my website, no I'm not trying to spam you, there are no ads on that page, I gain nothing from you using it)
It's jQuery enabled and I think it's simple to use (but I built it, so who knows). If you need an extensive explanation of how to use it, let me know so I can make it better. You can even browse over the source if you'd like.
The important part is the business logic that gets jQuery on the page:
//s used for the Script element
var s = document.createElement('script');
//r used for the Ready state
var r = false;
//set the script to the latest version of jQuery
s.setAttribute('src', 'http://code.jquery.com/jquery-latest.min.js');
//set the load/readystate events
s.onload = s.onreadystatechange = function()
{
/**
* LOAD/READYSTATE LOGIC
* execute if the script hasn't been ready yet and:
* - the ready state isn't set
* - the ready state is complete
* - note: readyState == 'loaded' executes before the script gets called so
* we skip this event because it wouldn't have loaded the init event yet.
*/
if ( !r && (!this.readyState || this.readyState == 'complete' ) )
{
//set the ready flag to true to keep the event from initializing again
r = true;
//prevent jQuery conflicts by placing jQuery in the zbooks object
window.zbooks = {'jQuery':jQuery.noConflict()};
//make a new zbook
window.zbooks[n] = new zbooks(c);
}
};
//append the jQuery script to the body
b.appendChild(s);
Can't you use a proper tool like Greasemonkey?
Related
I'm creating a Google Chrome extension that adds some extra functionality to Gmail by injecting a script that I've written. By 'injected script' please see this definition.
To do this, my content script matches on ["https://mail.google.com/mail/*"].
However, the problem with this is that this is the same URL for when Gmail is loading and has finished loading. As a result, my script appears to sometimes to get injected too early, while Gmail is still loading, leading to reference errors.
To try to circumvent this problem, I'm only running my script on load by doing:
window.addEventListener("load", runInjectedScript);
And in my content script (which does the injecting), it only injects when this conditional is true: if (window.self === window.top).
Yet, these measures do not seem to guarantee that my script will always get injected at the right time, once Gmail has finished loading and the inbox appears. How can I ensure that this happens? Are there any techniques that I haven't implemented?
To ensure injecting scripts when "inbox appears", take a look at MutationObserver, which provides a way to react to changes in the DOM, in particular for observing the input being inserted
You could potentially run a setInterval checking to see if the data you're intercepting in Gmail is available yet.
var gmailLoaded = setInterval(function () {
if (typeof GMAIL !== 'undefined') {
runInjectedScript();
clearInterval(gmailLoaded);
}
}, 100);
You'll need to replace GMAIL with whatever you're trying to reference from Gmail. You could also use the same approach above checking to see if the loading state is active, however that may add additional overhead.
I inspected the gmail page and once the inbox loads they add style="display: none" to the #loading element.
You could do some polling for that to change and then bootstrap your app.
function onInboxLoaded(cb) {
var pollInterval = setInterval(function() {
var elem = document.getElementById('loading');
if (elem && elem.style.display === 'none') {
clearInterval(pollInterval);
cb();
}
}, 100);
}
onInboxLoaded(function(){
// bootstrap app
})
Is there any way to know how far browser loaded the page?
Either by using JavaScript or browser native functions.
Based on the page status i want to build progress bar.
I'm not 100% sure this will work, but.. here is the theory:
First of all, don't stop JavaScript running until the page has loaded. Meaning, don't use window.ready document.ready etc..
At the top of the page initialise a JavaScript variable called loaded or something and set it to 0.
var loaded = 0;
Throughout the page increment loaded at different points that you consider to be at the correct percentages.
For example, after you think half the page would have been loaded in the code set loaded = 50;.
Etc..
As I say, this is just a concept.
Code:
function request() {
showLoading(); //calls a function which displays the loading message
myRequest = GetXmlHttpObject();
url = 'path/to/script.php';
myRequest.open('GET',url,true);
myRequest.onreadystatechange = function() {
if(myRequest.readyState == 4 && myRequest.status == 200) {
clearLoading(); //calls a function which removes the loading message
//show the response by, e.g. populating a div
//with the response received from the server
}
}
myRequest.send(null);
}
At the beginning of the request I call showLoading() which uses Javascript to dynamically add the equivalent of your preLoaderDiv. Then, when the response is received, I call clearLoading() which dynamically removes the equivalent of your preLoaderDiv.
You'll have to determine it yourself, and to do that you'll have to have a way to determine it. One possibility is to have dummy elements along the page, know their total, and at each point count how many are already present. But that will only give you the amount of DOM obtained, and that can be a very small part of the load time - most often than not, the browser is idle waiting for scripts and images.
I need to dynamically load several JavaScript file assets in a very specific order after a page has loaded. I'm trying to use onload, but this seems to fire before the asset has fully loaded. How should I adjust the below script to fire a proper callback to load the next script?
Note: Only needs to work in the latest Chrome, Firefox, Safari, and IE9.
function loadAssets() {
// Setup script
var scriptJS = document.createElement('script');
scriptJS.type = 'text/javascript';
scriptJS.src = objectUrl;
scriptJS.onload = loadAssetsNext();
// Begin insertion
var headerJS = document.getElementsByTagName('HEAD');
headerJS[0].appendChild(scriptJS);
},
function loadAssetsNext() {
// Increment object counter
objectsCount++;
// Test to see if you should call another item
if ((objectsCount) < objects.length) {
// Setup script
var scriptJS = document.createElement('script');
scriptJS.type = 'text/javascript';
scriptJS.src = nextObjectUrl;
// Declare callback to fire after script has fully loaded
scriptJS.onload = loadAssetsNext();
// Begin insertion
var headerJS = document.getElementsByTagName('HEAD');
headerJS[0].appendChild(scriptJS);
}
}
What I need is something like scriptJS.fullyLoaded = doStuff. Have no clue where to go from here though.
PS: jQuery is not an option or another library. You should be able to do this by slightly modifying the above script.
The reason your onload event is firing immediately is that you are calling it, not assigning it.
scriptJS.onload = loadAssetsNext();
This just assigns the returned value from the call to loadAssetsNext to the property onload of the scriptJS object. What you are intending to do is:
scriptJS.onload = loadAssetsNext;
That sets the onload handler to be the loadAssests function. This should take care of your issues.
I think the problem is that your scriptJS.onload = loadAssetsNext(); is placed before headerJS[0].appendChild(scriptJS);
That means that your scripts would load and get appended to the page like this:
load script 1
load script 2
load script 3
...
append script 3
append script 2
append script 1
So I think you should just reorder your script a little bit.
Background
I'm developing a bookmarklet which has dependencies on 3rd party libraries to be loaded into the DOM before my bookmarklet script can run. The heart of the problem I'm having is this dependency chain that I need to wait for before I can react, and I'm having issues getting it to fly in IE.
I cannot be sure JQuery is even on the page at this stage. It's something I check, and if it isn't there, then I have to create and inject this into the DOM.
The flow of control is something like this:
Load main library (jquery) into the DOM, I've attached an event listener to pick up on when it's actually in the DOM.
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = http:// ... jquerylibrary.js
e.addEventListener('load', callback);
Setup the callback function
var callback = function() {
//my stuff that wants to use jquery
}
This works, and it works well. I can chain jquery, some jquery plugins, then my code, and then I can be sure when my code runs (in real browser land) that it'll run.
The Crux of the problem
When IE > 9 (Which we must support) has a similar function ... attachEvent('onload',callback);
Thing that's similar, but doesn't appear to fire correctly when DOM elements are updated. Looking through MSDN's long list of events, it's not clear if any of those events are what I'm looking for.
What event does IE 7+ fire when the DOM is updated?
I've figured it out after reading some of the source of head.js. This calls for a bit of creative javascript magic.
The events we're interested in are onreadystatechange, and onload. You can attach a custom function to these to trigger update notifications when they insert into the page.
I've abstracted this into a function, where, if you pass it an element and a callback statement, it'll dump the element at the end of the page body and run the callback.
function loadElement(e, callback) {
if ( typeof(callback) === 'function' ) {
e.onreadystatechange = e.onload = function() {
var state = e.readyState;
if ( !callback.done && (!state || /loaded|complete/.test(state)) ) {
callback.done = true;
callback();
}
}
}
document.body.appendChild(e);
}
Though I've posted this answer to my problem, I'm interested in alternative approaches to this :).
I have a page that loads another window on button click. The loaded page has silverlight control on it, so it takes some time to load and get prepared before it can receive javascript calls.
What I need to do is to call a particular method of silverlight object right after the silverlight plugin gets loaded and is ready to interact with me.
Now, if the pop-up page was already opened then the code would be like that:
var slWin = window.open('PopupPage.html', 'WindowName');
var elem = slWin.document.getElementById('slControl');
elem.Content.SlObject.MethodA();
This works when the window is already opened because the control is already loaded and ready. I need to modify this code to handle the situation when the elem need some time to be prepared.
I tried to use jQuery's ready and load methods to add handlers to corresponding events, but with no particular lack. Here's the full snippet:
var slWin = window.open('', 'WindowName');
var elem = slWin.document.getElementById('slControl');
if (elem == null) {
slWin.location.href = 'PopupPage.aspx';
// this branch doesn't work
$(slWin).load(function () {
elem = slWin.document.getElementById('slControl');
elem.Content.SlObject.MethodA();
});
}
else {
// this branch works fine
elem.Content.SlObject.MethodA();
}
How do I solve this issue? I don't mind jQuery solutions.
This error is happening because the Silverlight object is not fully loaded when you are trying to access it.
Try to use the "onload" event of the silverlight object to dectect when it's ready to use. Here you have a link to the MSDN documentation:
http://msdn.microsoft.com/en-us/library/cc838107(v=vs.95).aspx
Hope it helps. :)