I like YUI sandbox but I have a problem with it. Consider a document with an iframe (for instance ckedior that I included) with an initialization script. It is always executed before the outer document's sections (don't know why).
And I need to call a function, that is initialized in YUI sandbox in the parent document. But it can never be initialized, because the execution starts in the iframe.
var getWordCount;
AUI().ready('aui-node', 'console', function(A) {
getWordCount = function (htmlData) {
var target = A.one('div.my-png-image');
target.one('div:fist-child').set('text', strip(htmlData).trim().split(/\s+/).length);
};
});
Without yui sandbox I'd just declared a function in the parent document, and even tough the execution starts in the iframe, the global function would exist and could be called from the iframe.
The problem might be AUI.ready, as it executes only when the whole HTML is loaded. This means the iFrame would load before the browser triggers the .ready-event.
You could try to test this by changing the AUI event you use to trigger your code, and put the code you want to run before the iFrame-load outside the AUI.ready.
If you need a certain element in dOM to be loaded before running, you could try checking if the element is loaded and then run your script, like in this example.
Could you perhaps create the IFrame from within the AUI().ready?
Related
A user script I'm working on successfully works on initially loaded GitHub page. Though it needs to be re-applied when GitHub is navigated with PJAX.
So I have to set up a listener for PJAX changes somehow and run initialize function again.
Patching unsafeWindow.history.pushState won't work because of Greasemonkey/Firefox security policy, it will throw
Error: Permission denied to access object
I've tried to examine page scripts and hook to ajaxComplete event of unsafeWindow.require('jquery'), but the listener wasn't triggered (possibly because of the same security policy, but error console was empty).
Is there a better solution than MutationObserver/DOMSubtreeModified on PJAX container element?
The script is targeted at Firefox/Greasemonkey, but if it will be workable for Chrome/Tampermonkey, this won't hurt.
Add a listener for "pjax:end" event on document:
$(document).on('pjax:end', ...)
FWIW a working example in vanilla js. As you can see a MutationObserver is also used because the site script updates the dynamic container sometimes twice (discarding the first one completely), so, after the pjax:end handler completes, a MutationObserver handler is attached to detect removal of the file wrapper, in which case the processing is repeated:
document.addEventListener('pjax:end', pageChangedHandler);
function pageChangedHandler() {
.......................
var ovr = document.querySelector('include-fragment.file-wrap');
if (ovr) {
new MutationObserver(function(mutations) {
mutations.forEach(m => {
var removed = m.removedNodes[0];
if (removed && removed.matches('.file-wrap')) {
this.disconnect();
pageChangedHandler();
}
});
}).observe(ovr.parentNode, {childList:true});
}
}
Here's a sample code:
HTML
<script> alert('This is alert!') </script>
JS
window.alert = function(data) //alert() over-riding
{
console.log("Alert over-ridden");
}
Issue:
HTML
<iframe src=javascript:alert('Iframealert')>
JS
window.alert = function(data) //alert() over-riding
{
console.log("Alert over-ridden"); //This doesn't execute - I mean, this over-ride function is not called when the above iframe alert is executed
}
I knew iframe in another document is not applicable for parent over-riding (due to same domain policy), but, the src JS execution happens only in the parent.
So, how do I over-ride alert() which is applicable to above iframe tag?
Update 1:
The HTML code is static, and I cannot make any modifications to it. I can only write some JS and append to the HTML.
Is there any way to over-ride the alert() of nested browsing window?
Nice question! The answer starts by looking at the rules for what happens when the src attribute is evaluated.
When this happens, the spec follows a number of steps during navigation. We end up step 14, which pertains to javascript: schemes.
Within that step, there are a series of sub-steps, one of which is:
Create a script, using script source as the script source, address as the script source URL, JavaScript as the scripting language, and the script settings object of the Window object of the active document of the browsing context being navigated.
The important thing here is "the Window object of the active document of the browsing context being navigated". Because you're navigating an <iframe>, you're actually dealing with a Nested Browsing Context, which has it's own window, so having overridden the parent window.alert makes no difference.
You can, however, override the alert of the inner window:
document.getElementById('myIframe').contentWindow.alert = function(msg) {
console.log('Overridden iframe: ' + msg);
}
This will only work for the javascript: scheme url presented to the src attribute, as at the time this code executes we were simply trying to get the address of the page to navigate the <iframe> to. When navigation actually occurs, a new Document object with it's own window is created in step 23 of the navigation steps, at which point you lose your overridden alert.
This also relies on setting the src attribute using JavaScript after you've overridden the alert, you can't use an inline src attribute on the element as the element needs to be in the page to get hold of it and it's contentWindow, and putting it in the page means the src will get evaluated.
Here's a fiddle demonstrating the overriding of the alert within the <iframe>'s src attribute.
I just discovered that the source of my issues is that the parent HTML makes calls to controls in a child IFRAME but is too quick about that and, sometimes, the super onLoad attempts to do so before the sub onLoad had a chance to add stuff to the DOM.
What can I do about it?
I've tried to set up some kind of feed-back from onLoad in the child. Failed miserably with so many strange errors that they can be summarized by dude, just please don't.
I've tried to set up a delayer, which is ugly of epic proportions and not 100% reliable.
EDIT:
In onload I do this:
var stuff = getReferenceToStuff();
var someDiv = stuff.contentWindow.document.getElementById("someDiv");
someDiv.className = "classy";
The problem is that sometimes the reference someDiv is null and sometimes (often when I reload the page by F5), it points to the correct element. I know it's because the contents of the IFRAME are a bit slower.
So my questions is this. How can I ensure that onload is postponed until the embedded IFRAME component's onload ensures that it's been loaded and all the components are there?
The onLoad event isn't always working correctly on document. It works correctly on each element though.
var iframes = document.getElementsByTagName('iframe'),
counter = 0,
max = iframes.length;
[].forEach.call(iframes, iLoaded);
function iLoaded() {
if (++counter === max) {
callback();
}
}
function callback() {
// All the iframes are loaded
}
When using window.onload all content of the body, including content of the iframes and all other resources like images, should be loaded before the onload is fired. However, some browsers have had problems with timing on firing onload, i.e. different browsers trigger the event in different stage of page parsing.
If you're using DOMContentLoaded or jQuery's $(document).ready(), only the HTML of the main page is loaded, but some resources are still under work (including iframe's content loading). I'm not aware what happens if you attach an inline listener for the iframe element itself.
If there's timing problems, maybe not trigger the function needing iframe reference in main window at all. Instead invoke that function in main window in irame's window.onload. But even this won't tackle the problem, if you're using some asynchronous technique to populate the iframe. In this case you need to invoke the function in main window after all requests have been completed.
(Now you maybe also know, what are the codesnippets I'd like to see in your post : ) ).
I have 2 solutions to your problem:
If you are on HTML5, use window.postMessage. Just message from iFrame to the parent in the onload of iFrame. Parent should register handler in <script> tag, that appears before iFrame.
Add a callback function to window in the '' tag before iFrame. This function is called by iFrame when it's load is complete. Here is the basic template.
Here is the sample template:
<script>
window.iframeCallback = function(message) {
// first clear the temp function we added to the window
// It is a bad practice to corrupt the global namespace
delete window.iframeCallback;
// you do your work here
};
</script>
..
..
<!-- iFrame should appear after the above script-->
<iframe/>
Essentially what I'd like to do is something to the effect of this:
window.location.href = "some_location";
window.onload = function() {
alert("I'm the new location and I'm loaded!");
};
Is there any way to have a callback when the window's new location is loaded? (The above code doesn't work.)
No, you cannot do it the way you want. Loading a new page closes the current document and starts loading a new document. Any code in your current document will no longer be active when the new page starts to load.
To have an event handler when the new document loads, you would either need to insert code into the new page's document or load the new document into an iframe and monitor the loading of the iframe from your current document.
Setting window.location.href to a new value tells the browser to simply go to the next page.
Like so:
window.location.href = "http://www.google.com/";
Once the browser goes to google.com, that's it, your Javascript is no longer executing, and there is no way for you to have any sort of callback, because the browser is no longer running your page.
So, the answer is no.
in regards to callback and js execution in new context - no (as per orig answer), but there are ways to access loaded elements without callback. For e.g. scrollIntoView on a loaded element, you can append the selector:
const href = '#someElem'
if(window.location.pathname !== '/') window.location.href = `/${href}`
else document.querySelector(href).scrollIntoView()
I am developing a Firefox add-on using XUL Overlay and want to call a specific js when the current page loads after entering the URL. I want to know which XUL element would be affected and should be used to call said JS, such as page or tab or window or ??? Also, which event would be best for the element? Or is my logic wrong?
Also,the js's function is to record tab title and/or url so i need to know when to call js and with corresponding event. Thanks.. :)
The XUL element you should be watching is the tabbrowser. In the browser window (which means also in overlays applied to the browser window) it can be accessed via the global gBrowser variable. If you want to know when a page finishes loading you can listen to the DOMContentLoaded event. Something like this (untested code):
// Declare an own namespace for extension's functions to avoid
// name conflicts with other extensions.
var MyExtension = {};
MyExtension.init = function()
{
gBrowser.addEventListener("DOMContentLoaded", MyExtension.onPageLoad, false);
};
MyExtension.onPageLoad = function(event)
{
// Get the document that loaded
var doc = event.originalTarget;
// Ignore frames that load
if (doc.defaultView != doc.defaultView.parent)
return;
// Ignore if this isn't the active tab
var browser = gBrowser.getBrowserForDocument(doc);
if (browser != gBrowser.selectedBrowser)
return;
alert("Page loaded in current tab: " + doc.defaultView.location.href);
};
// Wait for the browser window to finish loading before adding event listeners
window.addEventListener("load", MyExtension.init, false);
If you want to get notified earlier, when the address displayed in the URL bar changes, you can use a progress listener instead. You want to implement the method onLocationChange of the progress listener and leave the other methods empty. Note that this method is also called when the user switches to a different tab (this also causes a location bar change). Also: the parameter aURI passed to onLocationChange is an nsIURI instance. If you want the URL as a string you should look at aURI.spec.