How to use jQuery in Firefox Extension - javascript

I want to use jQuery inside a firefox extension,
I imported the library in the xul file like this:
<script type="application/x-javascript" src="chrome://myExtension/content/jquery.js"> </script>
but the $() function is not recognized in the xul file neither do the jQuery().
I googled about the problem and found some solutions but no one did work with me:
http://gluei.com/blog/view/using-jquery-inside-your-firefox-extension
http://forums.mozillazine.org/viewtopic.php?f=19&t=989465
I've also tried to pass the 'content.document' object(which refrences the 'document' object) as the context parameter to the jQuery function like this:
$('img',content.document);
but still not working,
does any one came across this problem before?

I use the following example.xul:
<?xml version="1.0"?>
<overlay id="example" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head></head>
<script type="application/x-javascript" src="jquery.js"></script>
<script type="application/x-javascript" src="example.js"></script>
</overlay>
And here is an example.js
(function() {
jQuery.noConflict();
$ = function(selector,context) {
return new jQuery.fn.init(selector,context||example.doc);
};
$.fn = $.prototype = jQuery.fn;
example = new function(){};
example.log = function() {
Firebug.Console.logFormatted(arguments,null,"log");
};
example.run = function(doc,aEvent) {
// Check for website
if (!doc.location.href.match(/^http:\/\/(.*\.)?stackoverflow\.com(\/.*)?$/i))
return;
// Check if already loaded
if (doc.getElementById("plugin-example")) return;
// Setup
this.win = aEvent.target.defaultView.wrappedJSObject;
this.doc = doc;
// Hello World
this.main = main = $('<div id="plugin-example">').appendTo(doc.body).html('Example Loaded!');
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
main.html(main.html() + ' - jQuery <b>' + $.fn.jquery + '</b>');
};
// Bind Plugin
var delay = function(aEvent) {
var doc = aEvent.originalTarget; setTimeout(function() {
example.run(doc,aEvent);
}, 1);
};
var load = function() {
gBrowser.addEventListener("DOMContentLoaded", delay, true);
};
window.addEventListener("pageshow", load, false);
})();

The following solution makes it possibile to use jQuery in contentScriptFile
(Targetting 1.5 Addon-sdk)
In your main.js:
exports.main = function() {
var pageMod = require("page-mod");
pageMod.PageMod({
include: "*",
contentScriptWhen: 'end',
contentScriptFile: [data.url("jquery-1.7.1-min.js") , data.url("notifier.js") , data.url("message.js")],
onAttach: function onAttach(worker) {
//show the message
worker.postMessage("Hello World");
}
});
};
In your message.js :
self.on("message", function(message){
if(message !== "undefined"){
Notifier.info(message);
}
});
Some pitfalls you need to watchs out for:
The order of the contentScriptFile array. if message.js would be placed first: jQuery won't be reconized.
Do not place a http:// url in the data.url (this does not work)!
All your javascript files should be in the data folder. (only main.js should be in lib folder)

There is an excellent article in the mozillaZine forums that describes this step-by-step: http://forums.mozillazine.org/viewtopic.php?f=19&t=2105087
I haven't tried it yet, though so I hesitate to duplicate the info here.

Turns out the current top-answer by #sunsean does not work as expected when it comes to handling multiple loads. The function should properly close over the document and avoid global state.
Also, you have to call jQuery.noConflict(true) to really avoid conflicts with other add-ons!
This is who I would write it (then again, I would avoid jquery (in add-ons) like the plague...).
First the overlay XUL
<?xml version="1.0"?>
<overlay id="test-addon-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="text/javascript" src="jquery.js"/>
<script type="text/javascript" src="overlay.js"/>
</overlay>
And then the overlay script:
// Use strict mode in particular to avoid implicitly var declarations
(function() {
"use strict";
// Main runner function for each content window.
// Similar to SDK page-mod, but without the security boundaries.
function run(window, document) {
// jquery setup. per https://stackoverflow.com/a/496970/484441
$ = function(selector,context) {
return new jq.fn.init(selector,context || document);
};
$.fn = $.prototype = jq.fn;
if (document.getElementById("my-example-addon-container")) {
return;
}
let main = $('<div id="my-example-addon-container">');
main.appendTo(document.body).text('Example Loaded!');
main.click(function() { //<--- added this function
main.text(document.location.href);
});
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
};
const log = Components.utils.reportError.bind(Components.utils);
// Do not conflict with other add-ons using jquery.
const jq = jQuery.noConflict(true);
gBrowser.addEventListener("DOMContentLoaded", function load(evt) {
try {
// Call run with this == window ;)
let doc = evt.target.ownerDocument || evt.target;
if (!doc.location.href.startsWith("http")) {
// Do not even attempt to interact with non-http(s)? sites.
return;
}
run.call(doc.defaultView, doc.defaultView, doc);
}
catch (ex) {
log(ex);
}
}, true);
})();
Here is a complete add-on as a gist. Just drop in a copy of jquery and it should be good to go.

I think this is what Eric was saying, but you can load Javascript from the URL directly.
javascript:var%20s=document.createElement('script');s.setAttribute('src','http://YOURJAVASCRIPTFILE.js');document.getElementsByTagName('body')[0].appendChild(s);void(s);
Im assuming you want your extension to load JQuery so you can manipulate the page elements easily? My company's labs has something that does this using Javascript directly here: http://parkerfox.co.uk/labs/pixelperfect

It may be bad practice, but have you considered including it inline?

Instead of
$('img',content.document);
you can try
$('img',window.content.document);
In my case it works.

Related

Check if JS plugin is loaded before executing code

I'd like some insight into a little problem I'm encountering with a custom JS plugin that I've made. It's reasonably simple, I'm using a plugin JS template to write out plugins, which I'll attach. I'm then initialising the plugin, and ideally need to figure out if there's a way I can check if the plugin JS file is loaded before doing anything:
plugin template
(function() {
this.MyPlugin = function() {
// default settings
const INBOUND_CONFIG = {
isEnabled: false
}
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(INBOUND_CONFIG, arguments[0]);
}
// custom public method
HoneycombInbound.prototype.getSettings = function() {
}
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
}
}());
With the above, I'd initialise the plugin in HTML as follows:
<script src="./my-plugin.js"></script>
<script>
const plugin = new MyPlugin()
// this doesn't work if the script file isn't linked:
if (plugin) {
// do something
}
</script>
Unfortunately, a simple check of plugin doesn't actually work if the JS file isn't loaded for instance, I've even tried something like: if (new MyPlugin()) {} with little hope.
I essentially need a way of not throwing an error in the browser. Any solution I can use to check correctly if it's loaded before executing?
A solution that has worked for me is to work with the WebAPI postMessage
I've used to communicate with the client, that the plugin is ready.
So on the plugin you will need something like this:
window.postMessage("The plugin is ready to go" );
And on your client you will need to add a new listener that can catch the message:
window.addEventListener("message", myMessage, false);
const myMessage = (event) => {
if (event.data === "The plugin is ready to go")
// This is your message so do your stuff here
...
}
EDIT
So to achieve this behavio, you will need to enable on the client sidethe initialization:
const myPlugin = {
init: () => {
// Initilize the plugin if you are using the DOM you can add a parameter with an id to mount it whenever you need it
//Otherwise just configure or initilize the variables that you will use
// And here you need to pass the messsage to tell the client that everyting is ready
// Here you need to configure the origin correctly depending on your needs check the documentation for more details
window.addEventListener("message", myMessage, false);
},
someFunctionality: () => {
//This pattern is to expose the funtionalities to the client so you can achieve any
}
};
So now on your client you just need to add your script and tell the plugin to initialize:
<script type='text/javascript' src='/myPlugin.js'></script>
<script>
window.addEventListener("message", myMessage, false);
const myMessage = (event) => {
if (event.data === "The plugin is ready to go")
// This is your message so do your stuff here
...
}
myPlugin.init();
</script>

Javascript file load fallback. Alternative to document.write()

You might be familiar with the good old Jquery load fallback:
<script>window.jQuery || document.write('<script src="https://example.com/jquery.js"></script>')</script>
But I read here and there: don’t use document.write, is bad for your health, it does not work on Chrome (It’s working for me, Chrome 78).
So I’m trying to replace it, but I’m not able to find a solution that will load synchronously the new js file, before DOM loaded is triggered.
And what ends happening with a DOM manipulation alternative is that the browser consideres the DOM is loaded and all $(document).ready() fail with “$ is not defined”.
function Jqfallback() {
var j = document.createElement('script');
j.src = 'https://example.com/jquery.js';
document.getElementsByTagName('head')[0].appendChild(j);
}
(window.jQuery || Jqfallback() );
No matter where I put this script, or the new JS file, which in this case ('head')[0] is already before all other JS which are in the body, it loads it “asyncronically”.
Is there another option or I continue rocking document.write() in late 2019?
It takes a bit of time to load and parse JQuery. So use a (small) timeout after appending the script.
This snippet wraps conditional loading in a immediately executed anonymous function:
(myScripting => {
if (!window.$) {
let j = document.createElement('script');
j.src = '//code.jquery.com/jquery-3.4.1.slim.min.js';
document.querySelector('head').appendChild(j);
setTimeout( myScripting, 200 );
} else {
myScripting();
}
})(JqIsLoadedSoMyScriptingCanStart);
// put your main scripting in here
function JqIsLoadedSoMyScriptingCanStart() {
// extra check
if (!window.$) {
alert("Sorry, JQuery is not loaded, can't continue");
return;
}
console.log("JQuery in place?");
console.log($("head script")[1]);
}
<script src="cantLoadThis"></script>
Place the code that uses jQuery in the onload() function.
var jQuery1 = document.createElement('script');
jQuery1.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js";
jQuery1.onload = function () {
var $ = window.jQuery;
$.when(
$.getScript("https://someOtherScript.js"), //if you need
$.Deferred(function (deferred) {
$(deferred.resolve);
})
).done(function () {
console.log("all scripts loaded!!");
doNextTask(); //some other code which uses jQuery
});
};
Append jQuery to your document in onreadystatechange
document.onreadystatechange = function () {
if (document.readyState == "complete") {
// document is ready.
document.head.appendChild(jQuery1);
}
}

javascript revealing module pattern and jquery

I'm trying to up my js foo and start to use the module patter more but I'm struggling.
I have a main page with a jquery-ui element that pops up a dialog that loads an ajax requested page for data entry. The below code is contained within the popup ajax page.
After the pop up is loaded the Chrome console is able to see and execute ProtoSCRD.testing() just fine. If I try to run that in the jQuery.ready block on the page, I get:
Uncaught ReferenceError: ProtoSCRD is not defined
Yet i can execute toggleTypeVisable() in the ready block and life is good. Can anyone shed some light?
$(document).ready(function() {
setHoodStyleState();
$('#hood-style').change(function(){
hstyle = $('#hood-style').val();
if ( hstyle.indexOf('Custom') != -1) {
alert('Custom hood style requires an upload drawing for clarity.');
}
setHoodStyleState();
});
setCapsState();
$('#caps').change(function(){
setCapsState();
});
setCustomReturnVisibility();
$('#return').change(function(){ setCustomReturnVisibility(); });
toggleTypeVisable();
$('#rd_type').change(function(){
toggleTypeVisable();
});
ProtoSCRD.testing();
});
function toggleTypeVisable(){
if ( $('#rd_type').val() == 'Bracket' ) {
$('.endcap-ctl').hide();
$('.bracket-ctl').show();
}
if ( $('#rd_type').val() == 'Endcap' ) {
$('.bracket-ctl').hide();
$('.endcap-ctl').show();
}
if ( $('#rd_type').val() == 'Select One' ) {
$('.bracket-ctl').hide();
$('.endcap-ctl').hide();
}
}
ProtoSCRD = (function($, w, undefined) {
testing = function(){
alert('testing');
return '';
}
getDom = function(){
return $('#prd-order-lines-cnt');
}
return {
testing: testing,
getDom: getDom
};
}(jQuery, window));
calling the popup dialog like so - which is in fact in another ready in a diff file on the parent page:
// enable prototype button
$( "#proto-btn" ).click(function(e){
e.preventDefault();
showPrototype();
});
I don't know if it will solve your problems at all, but you are definitely missing several var statements you really should have:
var ProtoSCRD = (function($, w, undefined) {
var testing = function(){
alert('testing');
return '';
};
var getDom = function(){
return $('#prd-order-lines-cnt');
};
return {
testing: testing,
getDom: getDom
};
}(jQuery, window));
IMHO, it's best practice to use var for every variable you declare. (Function declarations do so implicitly.)
But I really don't know if this will help solve anything. But it should store everything in its proper scope.
Update
Here's one possible issue: if the document is already ready (say this is loading at the end of the body), then perhaps jQuery is running this synchronously. Have you tried moving the definition of ProtoSCRD above the document.ready block?

GetElementById of ASP.NET Control keeps returning 'null'

I'm desperate having spent well over an hour trying to troubleshoot this. I am trying to access a node in the DOM which is created from an ASP.NET control. I'm using exactly the same id and I can see that they match up when looking at the HTML source code after the page has rendered. Here's my [MODIFIED according to suggestions, but still not working] code:
ASP.NET Header
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onchange = alert('test!!');
)
</script>
</asp:Content>
ASP.NET Body
<asp:TextBox ID="txtBox" runat="server"></asp:TextBox>
Resulting Javascript & HTML from above
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('MainContent_txtBox');
el.onchange = alert('test!!');
)
</script>
...
<textarea name="ctl00$MainContent$txtBox" id="MainContent_txtBox"></textarea>
I can only assume that the script is loading before the control id has been resolved, yet when I look at the timeline with Chrome's "Inspect Element" feature, it appears that is not the case. When I created a regular textarea box to test and implement the identical code (different id of course), the alert box fires.
What on earth am I missing here? This is driving me crazy >.<
EDIT: Wierd code that works, but only on the initial page load; firing onload rather than onchange. Even jQuery says that .ready doesn't work properly apparently. Ugh!!
$(document).ready(function() {
document.getElementById('<%= txtBox.ClientID %>').onchange = alert('WORKING!');
})
Assuming the rendered markup does appear in that order, the problem is that the element doesn't yet exist at the time your JavaScript is attempting to locate it.
Either move that JS below the element (preferably right at the end of the body) or wrap it in something like jQuery's document ready event handler.
Update:
In response to your edits, you're almost there but (as others have mentioned) you need to assign a function to the onchange event, not the return result of alert(). Something like this:
$(document).ready(function() {
// Might as well use jQuery to attach the event since you're already using
// it for the document ready event.
$('#<%= txtBox.ClientID %>').change(function() {
alert('Working!');
});
});
By writing onchange = alert('Working');, you were asking JavaScript to assign the result of the alert() method to the onchange property. That's why it was executing it immediately on page load, but never actually in response to the onchange event (because you hadn't assigned that a function to run onchange).
Pick up jQuery.
Then you can
$(function()
{
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onclick() { alert('test!!'); }
});
Other answers have pointed out the error (attempting to access DOM nodes before they are in the document), I'll just point out alternative solutions.
Simple method
Add the script element in the HTML below the closing tag of the element you wish to access. In its easiest form, put it just before the closing body tag. This strategy can also make the page appear faster as the browser doesn't pause loading HTML for script. Overall load time is the same however, scripts still have to be loaded an executed, it's just that this order makes it seem faseter to the user.
Use window.onload or <body onload="..." ...>
This method is supported by every browser, but it fires after all content is loaded so the page may appear inactive for a short time (or perhaps a long time if loading is dealyed). It is very robust though.
Use a DOM ready function
Others have suggested jQuery, but you may not want 4,000 lines and 90kb of code just for a DOM ready function. jQuery's is quite convoluted so hard to remove from the library. David Mark's MyLibrary however is very modular and quite easy to extract just the bits you want. The code quality is also excellent, at least the equal of any other library.
Here is an example of a DOM ready function extracted from MyLibrary:
var API = API || {};
(function(global) {
var doc = (typeof global.document == 'object')? global.document : null;
var attachDocumentReadyListener, bReady, documentReady,
documentReadyListener, readyListeners = [];
var canAddDocumentReadyListener, canAddWindowLoadListener,
canAttachWindowLoadListener;
if (doc) {
canAddDocumentReadyListener = !!doc.addEventListener;
canAddWindowLoadListener = !!global.addEventListener;
canAttachWindowLoadListener = !!global.attachEvent;
bReady = false;
documentReady = function() { return bReady; };
documentReadyListener = function(e) {
if (!bReady) {
bReady = true;
var i = readyListeners.length;
var m = i - 1;
// NOTE: e may be undefined (not always called by event handler)
while (i--) { readyListeners[m - i](e); }
}
};
attachDocumentReadyListener = function(fn, docNode) {
docNode = docNode || global.document;
if (docNode == global.document) {
if (!readyListeners.length) {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded',
documentReadyListener, false);
}
if (canAddWindowLoadListener) {
global.addEventListener('load', documentReadyListener, false);
}
else if (canAttachWindowLoadListener) {
global.attachEvent('onload', documentReadyListener);
} else {
var oldOnLoad = global.onload;
global.onload = function(e) {
if (oldOnLoad) {
oldOnLoad(e);
}
documentReadyListener();
};
}
}
readyListeners[readyListeners.length] = fn;
return true;
}
// NOTE: no special handling for other documents
// It might be useful to add additional queues for frames/objects
else {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded', fn, false);
return true;
}
return false;
}
};
API.documentReady = documentReady;
API.documentReadyListener = documentReadyListener;
API.attachDocumentReadyListener = attachDocumentReadyListener;
}
}(this));
Using it for your case:
function someFn() {
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
}
API.attachDocumentReadyListener(someFn);
or an anonymous function can be supplied:
API.attachDocumentReadyListener(function(){
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
};
Very simple DOM ready functions can be done in 10 lines of code if you just want one for a specific case, but of course they are less robust and not as reusable.

jQuery .ready in a dynamically inserted iframe

We are using jQuery thickbox to dynamically display an iframe when someone clicks on a picture. In this iframe, we are using galleria a javascript library to display multiple pictures.
The problem seems to be that $(document).ready in the iframe seems to be fired too soon and the iframe content isn't even loaded yet, so galleria code is not applied properly on the DOM elements. $(document).ready seems to use the iframe parent ready state to decide if the iframe is ready.
If we extract the function called by document ready in a separate function and call it after a timeout of 100 ms. It works, but we can't take the chance in production with a slow computer.
$(document).ready(function() { setTimeout(ApplyGalleria, 100); });
My question: which jQuery event should we bind to to be able to execute our code when the dynamic iframe is ready and not just it's a parent?
I answered a similar question (see Javascript callback when IFRAME is finished loading?).
You can obtain control over the iframe load event with the following code:
function callIframe(url, callback) {
$(document.body).append('<IFRAME id="myId" ...>');
$('iframe#myId').attr('src', url);
$('iframe#myId').load(function() {
callback(this);
});
}
In dealing with iframes I found good enough to use load event instead of document ready event.
Using jQuery 1.3.2 the following worked for me:
$('iframe').ready(function() {
$('body', $('iframe').contents()).html('Hello World!');
});
REVISION:!
Actually the above code sometimes looks like it works in Firefox, never looks like it works in Opera.
Instead I implemented a polling solution for my purposes. Simplified down it looks like this:
$(function() {
function manipIframe() {
el = $('body', $('iframe').contents());
if (el.length != 1) {
setTimeout(manipIframe, 100);
return;
}
el.html('Hello World!');
}
manipIframe();
});
This doesn't require code in the called iframe pages. All code resides and executes from the parent frame/window.
In IFrames I usually solve this problem by putting a small script to the very end of the block:
<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
fireOnReadyEvent();
parent.IFrameLoaded();
//]]>
</script>
</body>
This work most of the time for me. Sometimes the simplest and most naive solution is the most appropriate.
Following DrJokepu's and David Murdoch idea I implemented a more complete version.
It requires jQuery on both the parent and iframe and the iframe to be in your control.
iframe code:
var iframe = window.frameElement;
if (iframe){
iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow
var parent = window.parent;
$(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
var parent$ = parent.jQuery;
parent$(iframe).trigger("iframeloading");
$(function(){
parent$(iframe).trigger("iframeready");
});
$(window).load(function(){//kind of unnecessary, but here for completion
parent$(iframe).trigger("iframeloaded");
});
$(window).unload(function(e){//not possible to prevent default
parent$(iframe).trigger("iframeunloaded");
});
$(window).on("beforeunload",function(){
parent$(iframe).trigger("iframebeforeunload");
});
});
}
parent test code:
$(function(){
$("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
console.log(e.type);
});
});
Found the solution to the problem.
When you click on a thickbox link that open a iframe, it insert an iframe with an id of TB_iframeContent.
Instead of relying on the $(document).ready event in the iframe code, I just have to bind to the load event of the iframe in the parent document:
$('#TB_iframeContent', top.document).load(ApplyGalleria);
This code is in the iframe but binds to an event of a control in the parent document. It works in FireFox and IE.
This function from this answer is the best way to handle this as $.ready explicitly fails for iframes. Here's the decision not to support this.
The load event also doesn't fire if the iframe has already loaded. Very frustrating that this remains a problem in 2020!
function onIframeReady($i, successFn, errorFn) {
try {
const iCon = $i.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $i.contents();
if($con.length === 0) { // https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch(e) { // accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$i.on("load.jqueryMark", () => {
try {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href !== bl || src === bl || src === "") {
$i.off("load.jqueryMark");
callCallback();
}
} catch(e) {
errorFn();
}
});
};
if(iCon.document.readyState === compl) {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch(e) {
errorFn();
}
}
Basically what others have already posted but IMHO a bit cleaner:
$('<iframe/>', {
src: 'https://example.com/',
load: function() {
alert("loaded")
}
}).appendTo('body');
Try this,
<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>
All you need to do then is create the JavaScript function testframe_loaded().
I'm loading the PDF with jQuery ajax into browser cache. Then I create embedded element with data already in browser cache. I guess it will work with iframe too.
var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
url: url,
cache: true,
mimeType: 'application/pdf',
success: function () {
// display cached data
$(scroller).append('<embed type="application/pdf" src="' + url + '" />');
// hide spinner
$.mobile.hidePageLoadingMsg();
}
});
You have to set your http headers correctly as well.
HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
This was the exact issue I ran into with our client. I created a little jquery plugin that seems to work for iframe readiness. It uses polling to check the iframe document readyState combined with the inner document url combined with the iframe source to make sure the iframe is in fact "ready".
The issue with "onload" is that you need access to the actual iframe being added to the DOM, if you don't then you need to try to catch the iframe loading which if it is cached then you may not. What I needed was a script that could be called anytime, and determine whether or not the iframe was "ready" or not.
Here's the question:
Holy grail for determining whether or not local iframe has loaded
and here's the jsfiddle I eventually came up with.
https://jsfiddle.net/q0smjkh5/10/
In the jsfiddle above, I am waiting for onload to append an iframe to the dom, then checking iframe's inner document's ready state - which should be cross domain because it's pointed to wikipedia - but Chrome seems to report "complete". The plug-in's iready method then gets called when the iframe is in fact ready. The callback tries to check the inner document's ready state again - this time reporting a cross domain request (which is correct) - anyway it seems to work for what I need and hope it helps others.
<script>
(function($, document, undefined) {
$.fn["iready"] = function(callback) {
var ifr = this.filter("iframe"),
arg = arguments,
src = this,
clc = null, // collection
lng = 50, // length of time to wait between intervals
ivl = -1, // interval id
chk = function(ifr) {
try {
var cnt = ifr.contents(),
doc = cnt[0],
src = ifr.attr("src"),
url = doc.URL;
switch (doc.readyState) {
case "complete":
if (!src || src === "about:blank") {
// we don't care about empty iframes
ifr.data("ready", "true");
} else if (!url || url === "about:blank") {
// empty document still needs loaded
ifr.data("ready", undefined);
} else {
// not an empty iframe and not an empty src
// should be loaded
ifr.data("ready", true);
}
break;
case "interactive":
ifr.data("ready", "true");
break;
case "loading":
default:
// still loading
break;
}
} catch (ignore) {
// as far as we're concerned the iframe is ready
// since we won't be able to access it cross domain
ifr.data("ready", "true");
}
return ifr.data("ready") === "true";
};
if (ifr.length) {
ifr.each(function() {
if (!$(this).data("ready")) {
// add to collection
clc = (clc) ? clc.add($(this)) : $(this);
}
});
if (clc) {
ivl = setInterval(function() {
var rd = true;
clc.each(function() {
if (!$(this).data("ready")) {
if (!chk($(this))) {
rd = false;
}
}
});
if (rd) {
clearInterval(ivl);
clc = null;
callback.apply(src, arg);
}
}, lng);
} else {
clc = null;
callback.apply(src, arg);
}
} else {
clc = null;
callback.apply(this, arguments);
}
return this;
};
}(jQuery, document));
</script>

Categories

Resources