2 versions of jQuery -> 2 documents. why? - javascript

I have a small page:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script type="text/javascript" src="jquery-1.4.2.js"></script>
<script type="text/javascript" src="temp.js"></script>
</head>
<body>
<p>foo</p>
<p>bar</p>
</body>
</html>
and I'm trying to load two different versions of jQuery:
// temp.js
jQueryScriptOutputted = false;
initJQuery = function() {
//if the jQuery object isn't available
if (typeof(myjQuery) == 'undefined') {
if (!jQueryScriptOutputted) {
//only output the script once..
jQueryScriptOutputted = true;
//output the script (load it from google api)
document.write("<script type=\"text/javascript\" src=\"jquery-1.6.4.js\"></script>");
document.write("<script type=\"text/javascript\">var myjQuery = $.noConflict(true);</script>");
}
setTimeout("initJQuery()", 50);
} else {
myjQuery(function() {
// Check jQuery versions
console.log('myjQuery version = ' + myjQuery().jquery);
console.log('$ version = ' + $().jquery);
console.log('jQuery version = ' + jQuery().jquery);
// Get the data of the actual poll
document.write("Where is foo and bar?!?");
});
}
}
initJQuery();
but it seems that this loads two different documents. I mean, when you open the page, the paragraphs get lost. How come?!?

Calling document.write after the page has loaded will overwrite the entire page with the document.write parameter. Consider using something else like $().append or $().html to change the markup.
i.e.
myjQuery(function() {
$('body').append("<p>Where is foo and bar?!?</p>");
});

You must only load one version or the other.
In other words, only have one jquery library installed to the page.

The problem is that you are writing the <script> tags to the document and not the <head>
Please see these instructions for full information on how to dynamically load jQuery.
The tutorial explains how to do it really well.
Hope this helps.

Related

What's wrong with this simple Safari Extension code?

I'm creating a Safari extension that will stay in Safari's menubar, and upon being clicked, it will open all links containing a certain string. However, it's not working.
This is what my extension builder screen looks like: http://i.imgur.com/xRXB1.png
I don't have any external scripts set as I have the script in my HTML file, because I only want it to run when clicked.
And I have a global.html page with the following code in it:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<script>
safari.application.addEventListener("comnand", performCommand, false);
Function performCommand(event) {
if (event.command == "open-designs") {
$(document).ready(function() {
$('a[href*="/Create/DesignProduct.aspx?"]').each(function() {
window.open($(this).attr('href'),'_blank');
});
});
}
}
</script>
</body>
</html>
Should this not work? I'm allowed to mix jQuery and JS write, as jQuery is JS? And isn't that how I'd target the links?
The problem is that your extensions Global page does not have direct access to the currently loaded page's DOM. To be able to achieve what you need, you'll have to use an Injected Script and use the messaging proxy to talk to the page.
For instance, your global would look like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<script>
$(document).ready(function() {
safari.application.addEventListener("command", performCommand, false);
});
function performCommand(event) {
if (event.command == "open-designs") {
safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("open-designs", "all");
}
}
</script>
</body>
</html>
And then, in Extension Builder, you'd need to add two "Start Scripts", one is jquery, the other, a new file that gets loaded into the page and looks similar to this:
function extensionname_openAll(event)
{
if (event.name == 'open-designs')
{
$('a[href*="/Create/DesignProduct.aspx?"]').each(function(index,elem) {
window.open($(elem).attr('href'),'_blank');
});
}
}
safari.self.addEventListener("message", extensionname_openAll, true);
One clear thing I'm seeing is that your $(document).ready() function is located within another function. This essentially alleviates the need for a $(document).ready() provided you only call that function once the DOM and jQuery are fully loaded.
Rearrange your code to only add the event listener once the DOM and jQuery are loaded. That is what you use the $(document).ready() callback for.
In addition there is one more issue I see with the callback function for .each(). That function needs to handle two parameters the index and the element that it references. A call to each() iterates over a collection of elements. For each element entering the callback function, its index is passed as a parameter and also the element itself that is located at that index. Check out the documentation for more info.
$(document).ready(function() {
safari.application.addEventListener("command", performCommand, false);
console.log("Document is ready to go!");
});
function performCommand(event) {
console.log("event recieved");
if (event.command == "open-designs") {
console.log("got 'open-designs' event");
$('a[href*="/Create/DesignProduct.aspx?"]').each(function(index,elem) {
console.log("opening window", index, elem);
window.open($(elem).attr('href'),'_blank');
});
}
}
You use the $(document).ready() callback as an indication that your DOM is ready and jQuery has been initialized. Once you know everything is ready, you can setup your event listener.
The function performCommand() can not be called before the listener is added (unless there is some other reference to it).

Result of getElementById is null?

Just struggling with a Javascript class being used as a method for some cometishian code, how do I have a constructor for this code? The following code is invalid:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<link rel="Stylesheet" href="gStyle.css" />
<script type="text/javascript" language="javascript">
// Gantt chart object
function ganttChart(gContainerID) {
this.isDebugMode = true;
this.gContainer = document.getElementById(gContainerID);
if (this.isDebugMode) {
this.gContainer.innerHTML += "<div id=\"gDebug\">5,5 | 5.1</div>";
}
}
var myChart = new ganttChart("chart1");
</script>
</head>
</html>
<body>
<div id="chart1" class="gContainer"></div>
</body>
</html>
this.gContainer is null
That is because you are running the script before the page is ready, i.e. chart1 doesn't exist yet when you call new ganttChart("chart1");. Wrap the code inside window.onload = function() { } or run it at the bottom of the page.
The problem is that your script is running too early, it's looking for an element that doesn't exist in the DOM yet, either run your script onload, or place it at the end of the <body> so your id="chart1" element is there to be found when it runs.
Problem is that you run your code before the page has loaded yet, and thus the DOM element with id chart1 does not exist at the moment the code is executed.
use
window.onload = function(){myChart = new ganttChart("chart1");};
Note that using window.onload like that will override all previously stated window.onload declarations. Something along the following lines would be better:
<script type="text/javascript">
var prevOnload = window.onload || function () {};
window.onload = function () {
prevOnload();
// do your stuff here
};
</script>
Also, untill al images are fully loaded onload will not trigger, consider using jquery & $(document).ready or similar.
:)
Regards,
Pedro

Document.links empty after calling document.close

When you execute following example using Firefox 3:
<html>
<head>
<script language="javascript" type="text/javascript">
<!--
function openWindow(){
var w = window.open('', 'otherWin', 'width=600,height=600');
w.document.write(document.getElementsByTagName("html")[0].innerHTML);
w.document.close();
reportLinks(w.document.links);
}
function reportLinks(links){
var report = 'links: '+links.length;
for (var i=0;i<links.length;i++){
report += '\n (link='+links[i].href+')';
}
alert(report);
}
//-->
</script>
</head>
<body>
<p>Open Same Content and Show Links Report</p>
<p>Show Links Report</p>
</body>
</html>
You will see that both the number of links shown when clicking on 'Show Links Report' as when clicking on 'Open Same Content and Show Links Report' will be 2. However when having an external JavaScript file reference from this page the behavior seems different (just make an empty file some.js if you want). When clicking 'Open Same Content and Show Links Report' the number of links will be 0.
<html>
<head>
<script language="javascript" type="text/javascript" src="some.js"></script>
<script language="javascript" type="text/javascript">
<!--
function openWindow(){
var w = window.open('', 'otherWin', 'width=600,height=600');
w.document.write(document.getElementsByTagName("html")[0].innerHTML);
w.document.close();
reportLinks(w.document.links);
}
function reportLinks(links){
var report = 'links: '+links.length;
for (var i=0;i<links.length;i++){
report += '\n (link='+links[i].href+')';
}
alert(report);
}
//-->
</script>
</head>
<body>
<p>Open Same Content and Show Links Report</p>
<p>Show Links Report</p>
</body>
</html>
It is probably a matter of loading the page and the moment that reportLinks executed exactly. I assume that the external some.js is added that the document is not completely build up. Is there a way that I can register this reportLinks call for onload event so that I can be sure that document.links is complete?
By the way the example works fine in both cases with Google Chrome.
(added after answer1)
As suggested by Marcel K. I rewrote the example, added also the code the way I really would like to have the thing going. And now testing it, and this simple example seems to work with Firefox and with Chrome.
<html>
<head>
<script type="text/javascript" src="some.js"></script>
<script type="text/javascript">
<!--
function openWindow(){
var w = window.open('', 'otherWin', 'width=600,height=600');
w.document.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n<html>\n'+
document.getElementsByTagName("html")[0].innerHTML+'\n</html>');
w.onload=function(){
reportLinks(w.document.links);
};
w.document.close();
}
function reportLinks(links){
var report = 'links: '+links.length;
for (var i=0;i<links.length;i++){
report += '\n (link='+links[i].href+')';
}
alert(report);
}
//-->
</script>
</head>
<body>
<p>Open Same Content and Show Links Report</p>
<p>Show Links Report</p>
</body>
</html>
I had hoped with this simple example to show a simple case of the actual code I am writing. A print preview screen of complicated html in which I want to disable all hrefs once opened. But in that one the onload handler is never called... How can I register an onload handler in this case in the most robust way?
Many thanks,
Marcel
As I said in a comment, this is a very strange issue. But I think it happens because the inclusion of an external script causes a delay in page rendering (of the new page) and its DOM might not be ready to inspect.
My suspicion is supported by the fact that adding the (new) defer attribute seems to solve this issue:
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed.
The defer attribute can be set on the original page, as you want an exact copy of it. You can set it if it doesn't matter where a script is being included (e.g., when using document.write in your included file it does matter at which place you include it).
As defer is a Boolean attribute, it is activated when it is simply present (defer) or (when using XHTML) set to itself (defer="defer"). In your case, the script inclusion would read:
<script type="text/javascript" src="some.js" defer></script>
Update regarding your update: you should still insert a Doctype in the main page (consider using the HTML 5 one).
And I think the way you attached your onload event is the best you can do.
But considering the goal you want to achieve (a print preview without hyperlinks): you can also use the "print" media attribute and style hyperlinks like text; that's way more easy than the thing you are doing and it works when JavaScript is disabled.
The only way I could make the example above work portable over Firefox, Chrome and IE is by registering the onload listener through inlined JavaScript in the HTML loaded in the popup window. Following example code shows how.
<html>
<head>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<p>Open Same Content and Show Links Report</p>
<p>Show Links Report</p>
</body>
</html>
This page uses a script in script.js file. Following shows the content of that file.
function openWindow(){
var w = window.open('', 'otherWin', 'width=600,height=600');
w.document.write(
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n<html>\n'+
document.getElementsByTagName("html")[0].innerHTML+
'\n <script type="text/javascript">\n'+
' function addOnloadListener(listener){\n'+
' if (window.addEventListener) {\n'+
' window.addEventListener("load", listener, false);\n'+
' } else {\n'+
' window.attachEvent("onload",listener);\n'+
' }\n'+
' }\n'+
' addOnloadListener(function(){reportLinks(document.links);});\n'+
' </script>\n'+
'</html>');
w.document.close();
}
function reportLinks(links){
var report = 'links: '+links.length;
for (var i=0;i<links.length;i++){
report += '\n (link='+links[i].href+')';
}
alert(report);
}
When putting the function addOnloadListener directly in the JavaScript file (not inlined in the page) it doesn't work in IE6 because, I believe, it cannot handle the order of script entries correctly. When addOnloadListener was not inlined the inlined call to addOnloadListener didn't work, it simply couldn't find the function in the earlier:
<script type="text/javascript" src="script.js"></script>
The code is only a simple example that doesn't really do a lot. I used it for disabling all links in a print preview popup page.
A simpler way to register an onload listener for a popup window portable over browser is always welcome.
Thanks,
Marcel

Can't write to dynamic iframe using jQuery

My goal is to dynamically create an iframe and write ad JavaScript into it using jQuery (e.g. Google AdSense script). My code works on Chrome, but fails intermittently in Firefox i.e. sometimes the ad script runs and renders the ad, and other times it doesn't. When it doesn't work, the script code itself shows up in the iframe.
My guess is these intermittent failures occur because the iframe is not ready by the time I write to it. I have tried various iterations of iframe_html (my name for the function which is supposed to wait for the iframe to be ready), but no luck. Any help appreciated!
PS: I have read various threads (e.g. jQuery .ready in a dynamically inserted iframe). Just letting everyone know that I've done my research on this, but I'm stuck :)
Iteration 1:
function iframe_html(html){
$('<iframe name ="myiframe" id="myiframe"/>').appendTo('#maindiv');
$('#myiframe').load(
function(){
$('#myiframe').ready( function(){
var d = $("#myiframe")[0].contentWindow.document;
d.open();
d.close();
d.write(html);
});
}
);
};
Iteration 2:
function iframe_html(html){
$('<iframe id="myiframe"/>').appendTo('#maindiv').ready(
function(){
$("#myiframe").contents().get(0).write(html);
}
);
};
Honestly, the easiest and most reliable way I have found when dealing with the load events on iframes uses the "onload" attribute in the actual iframe tag. I have never had much of a problem with setting the content once the "onload" event fires. Here is an example:
<html>
<head>
<script type='text/javascript' src='jquery-1.3.2.js'></script>
<script type='text/javascript'>
$(function() {
var $iframe = $("<iframe id='myiframe' name='myiframe' src='iframe.html' onload='iframe_load()'></iframe>");
$("body").append($iframe);
});
function iframe_load() {
var doc = $("#myiframe").contents()[0];
$(doc.body).html("hi");
}
</script>
</head>
<body></body>
</html>
The problem with this is that you have to use attribute tags and global function declarations. If you absolutely CAN'T have one of these things, I haven't had problems with this (although it doesn't look much different than your attempts, so I'm not sure):
<html>
<head>
<script type='text/javascript' src='jquery-1.3.2.js'></script>
<script type='text/javascript'>
$(function() {
var $iframe = $("<iframe id='myiframe' name='myiframe' src='iframe.html'></iframe>");
$iframe.load(iframe_load);
$("body").append($iframe);
});
function iframe_load() {
var doc = $("#myiframe").contents()[0];
$(doc.body).html("hi");
}
</script>
</head>
<body></body>
</html>
This is one of the most frustrating parts of the DOM and JavaScript - my condolences are with you. If neither of these work, then open up Firebug and tell me what the error message is.
false.html:
<html>
<head><title></title></head>
<body></body>
</html>
JS:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript">
function iframe_html(html)
{
var id = "myiframe_" + ((new Date()).getTime());
$('<iframe src="false.html" name ="'+id+'" id="'+id+'" />').appendTo('#maindiv');
var loadIFrame = function()
{
var elIF = window.document.frames[id];
if (elIF.window.document.readyState!="complete")
{
setTimeout(loadIFrame, 100);
return false;
}
$(elIF.window.document).find("body").html(html);
}
loadIFrame();
};
$(function(){
iframe_html("<div>hola</div>");
});
</script>
</head>
<body>
<div id="maindiv"></div>
</body>
</html>
then please see this link

Why does appending a <script> to a dynamically created <iframe> seem to run the script in the parent page?

I'm attempting to create an <iframe> using JavaScript, then append a <script> element to that <iframe>, which I want to run in the context of the <iframe>d document.
Unfortunately, it seems I'm doing something wrong - my JavaScript appears to execute successfully, but the context of the <script> is the parent page, not the <iframe>d document. I also get a 301 Error in Firebug's "Net" tab when the browser requests iframe_test.js, though it then requests it again (not sure why?) successfully.
This is the code I'm using (live demo at http://onespot.wsj.com/static/iframe_test.html):
iframe_test.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title><iframe> test</title>
</head>
<body>
<div id="bucket"></div>
<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#bucket').append('<iframe id="test"></iframe>');
setTimeout(function() {
var iframe_body = $('#test').contents().find('body');
iframe_body.append('<scr' + 'ipt type="text/javascript" src="http://onespot.wsj.com/static/iframe_test.js"></scr' + 'ipt>');
}, 100);
});
</script>
</body>
</html>
iframe_test.js
$(function() {
var test = '<p>Shouldn\'t this be inside the <iframe>?</p>';
$('body').append(test);
});
One thing that seems unusual is that the the code in iframe_test.js even works; I haven't loaded jQuery in the <iframe> itself, only in the parent document. That seems like a clue to me, but I can't figure out what it means.
Any ideas, suggestions, etc. would be much appreciated!
Had the same problem, took me hours to find the solution.
You just need to create the script's object using the iframe's document.
var myIframe = document.getElementById("myIframeId");
var script = myIframe.contentWindow.document.createElement("script");
script.type = "text/javascript";
script.src = src;
myIframe.contentWindow.document.body.appendChild(script);
Works like a charm!
I didn't find an answer to my original question, but I did find another approach that works even better (at least for my purposes).
This doesn't use jQuery on the parent page (which is actually a good thing, as I'd prefer not to load it there), but it does load jQuery in the <iframe> in an apparently completely valid and usable way. All I'm doing is writing over the <iframe>'s document object with a new one created from scratch. This allows me to simply include a <script> element in a string which I then write to the <iframe>'s document object.
The code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>frame</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript">
// create a new <iframe> element
var iframe = document.createElement('iframe');
// append the new element to the <div id="bucket"></div>
var bucket = document.getElementById('test');
bucket.appendChild(iframe);
// create a string to use as a new document object
var val = '<scr' + 'ipt type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></scr' + 'ipt>';
val += '<scr' + 'ipt type="text/javascript"> $(function() { $("body").append("<h1>It works!</h1>"); }); </scr' + 'ipt>';
// get a handle on the <iframe>d document (in a cross-browser way)
var doc = iframe.contentWindow || iframe.contentDocument;
if (doc.document) {
doc = doc.document;
}
// open, write content to, and close the document
doc.open();
doc.write(val);
doc.close();
</script>
</body>
</html>
I hope this helps someone down the road!
The answer to the original question is simple - the execution of the script is done by jquery, and since jquery is loaded in the top frame, this is where the script runs too, no matter where you are appending it. A smarter implementation of jquery can no doubt be made to use the correct window object, but for now things are how they are.
As to the workarounds, you already have two good answers (even if one is your own). What I might add is that you can use one of those workarounds to include jquery.js in the iframe, and then get that jquery object instead of the top one to insert your additional markup... but that may very well be overkill too.

Categories