I'm playing around with the following code. The intention is to open a new window and create a basic document from scratch, rather than loading it from file.
function openNew() {
var win = window.open('', 'new') ;
win.document.write('<html><head><title>New</title></head><body><div id="wrap">Hello World</div></body></html>') ;
var head = win.document.getElementsByTagName('head')[0] ;
var style = win.document.createElement('link') ;
style.rel = "stylesheet" ;
style.type = "text/css" ;
style.href = "/css/main.css" ;
head.appendChild(style) ;
var script = win.document.createElement('script') ;
script.type = "text/javascript" ;
script.src = "/js/main.js" ;
head.appendChild(script) ;
return false ;
}
The document.write part seems to work, but the rest does nothing. Or at least nothing that I can tell, I get no errors or anything. I tried to just write the script part in the document.write with the rest of the document but that didn't help. Also tried using innerHTML instead of write() but that didn't work at all, nor did creating the whole document (html, head and body tags) with createElement.
If it has any importance, the script that needs to be loaded is the same one that does the opening in the first place. I don't know if that somehow creates a conflict though I'm not sure why it would do that. Just mentioning it for good measure.
Is this possible to do?
Trying closing the document, before accessing it.
var win = window.open('...');
win.document.open();
win.document.write('...');
win.document.close();
Other option is to build up the string and just add it all at once and not append to it.
Related
I hava some js code below,what's the data in src? how does it works?
script.src = "data:text/javascript,inc++"
details:
<script>
//Initialize the "inc" to zero.
var inc = 0;
//Get the HEAD element from the document.
var head = document.documentElement.firstChild;
//Create and initialize SCRIPT elements in a loop,
//they will execute 2 times of the "inc++" code.
for (var i = 0; i < 2; i++) {
var script = document.createElement("script");
script.src = "data:text/javascript,inc++"; // how does the data works?
head.insertBefore(script, head.firstChild);
script.onload = function () {
console.log(inc);
};
};
</script>
the src value should be setted a url,but this does not. why so?
From the MDN docs for the script tag:
src
This attribute specifies the URI of an external script; this can be used as an alternative to embedding a script directly within a document.
⚠️ If a script element has a src attribute specified, it should not have a script embedded inside its tags.
How can you embed a script directly within a document (other than embedding it between <script> tags)?
Here's another excerpt from MDN docs, on data URI:
Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents.
data:text/javascript indicates that it's a javascript document. Other type of interest is data:text/html, you can also do something like data:text/html,<script>/* some js code */</script>
I have seen it being more commonly used to embed small image in css, i.e
div {
background: url(data:image/png;base64,....); <-- image encoded in base64
}
So your example is basically equivalent to:
<script>
var inc = 0;
for (var i = 0; i < 2; i++) {
var script = document.createElement("script");
inc++;
console.log(inc);
};
</script>
Your excerpt of code perhaps is meant to explain how script tags behave.
I've created this jsbin with a demo of my problem.
var textFromHTTPRequest = '<html>'
+ '<head>'
+ '<script>'
+ 'function myFunc() { alert("works"); }'
+ '</script>'
+ '</head>'
+ '<body>'
+ 'test'
+ '</body>'
+ '</html>';
// Parse the text to dom element.
var parser = new DOMParser();
var el = parser.parseFromString(textFromHTTPRequest, "text/html");
// Get the script and contents of body.
var menuBody = el.firstChild.querySelectorAll('body > *');
var menuScript = el.firstChild.querySelector('head script');
// Insert the script into current head tag.
document.head.appendChild(menuScript);
// Insert the contents of body into current body.
for (var i = 0; i < menuBody.length; ++i) {
document.body.appendChild(menuBody[i]);
}
The scenario it is replicating is using Fetch to pull up a HTML document.
The code parses the text, and then pulls out the body contents. Something in the body uses an inline onmouseover. So I also need to pull in the script tag.
But the onmouseover now doesn't work.
Uncaught ReferenceError: myFunc is not defined
But if you use the Chrome Inspector - you can see the script has indeed been added.
(Please, let's not worry too much about why the page being fetched uses an onmouseover, I can't help that)
The browser will not "use" script that was not created by hitself.
You have 2 options:
1- create a new script and use the other's .innerHTML
var menuScript = document.createElement('script');
menuScript.innerHTML = el.firstChild.querySelector('head script').innerHTML;
jsBin: http://jsbin.com/dejerolozu/1/edit?html,css,js,output
2- use eval
You could run eval on that script, otherwise it will not be globalised by the browser:
// Insert the script into current head tag.
eval(menuScript.textContent);
jsBin: http://jsbin.com/zuwolelozo/1/edit?html,css,js,output
I fixed it myself by using createElement and copying the script contents into that. That works. That'll do.
var menuScript = el.firstChild.querySelector('head script');
var script = document.createElement( 'script' );
script.innerHTML = menuScript.innerHTML;
// Insert the script into current head tag.
document.head.appendChild(script);
I am trying to write a HTML page that asks users a series of questions. The answers to these questions are evaluated by my JavaScript code and used to determine which additional JavaScript file the user needs to access. My code then adds the additional JavaScript file to the head tag of my HTML page. I don't want to merge the code into a single JavaScript file because these additional files are large enough to be a nightmare if they're together, and I don't want to add them all to the head when the page first loads because I will have too many variable conflicts. I'm reluctant to redirect to a new webpage for each dictionary because this will make a lot of redundant coding. I'm not using any libraries.
I begin with the following HTML code:
<head>
<link rel="stylesheet" type="text/css" href="main.css">
<script src="firstSheet.js" type="text/JavaScript"></script>
</head>
//Lots of HTML.
<div id="mainUserMenu">
</div>
And I have the following JavaScript function:
function thirdLevelQuestions(secondLevelAnswer) {
//Code here to calculate the variables. This part works.
activeDictionary = firstKey + secondKey + '.js';
//Changing the HTML header to load the correct dictionary.
document.head.innerHTML = '<link rel="stylesheet" type="text/css" href="main.css"><script src="' + activeDictionary + '" type="text/JavaScript"></script><script src="firstSheet.js" type="text/JavaScript"></script>';
//for loop to generate the next level of buttons.
for (var i = 0; i < availableOptions.length; i++) {
document.getElementById('mainUserMenu').innerHTML += '<button onclick="fourthLevelQuestions(' + i + ')">' + availableOptions[i] + '</button>';
}
}
This creates the buttons that I want, and when I inspect the head element I can see both JavaScript files there. When I click on any of the buttons at this level they should call a function in the second file. Instead Chrome tells me "Uncaught ReferenceError: fourthLevelQuestions is not defined" (html:1). If I paste the code back into firstSheet.js the function works, so I assume the problem is that my HTML document is not actually accessing the activeDictionary file. Is there a way to do this?
What Can be done
You are trying to load Javascript on Demand. This has been a well thought out problem lately and most of the native solutions didn't work well across bowser implementations. Check a study here with different solutions and background of the problem explained well.
For the case of large web applications the solution was to use some javascript library that helped with modularising code and loading them on demand using some script loaders. The focus is on modularizing code and not in just script loading. Check some libraries here. There are heavier ones which includes architectures like MVC with them.
If you use AJAX implementation of jQuery with the correct dataType jQuery will help you evaluate the scripts, they are famous for handling browser differences. You can as well take a look at the exclusive getScript() which is indeed a shorthand for AJAX with dataType script. Keep in mind that loading script with native AJAX does not guarantee evaluation of the javascript included, jQuery is doing the evaluation internally during the processing stage.
What is wrong
What you have done above might work in most modern browsers (not sure), but there is an essential flaw in your code. You are adding the script tags to your head using innerHTML which inserts those lines to your HTML. Even if your browser loads the script it takes a network delay time and we call it asynchronous loading, you cannot use the script right away. Then what do you do? Use the script when its ready or loaded. Surprisingly you have an event for that, just use it. Can be something like:
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.onreadystatechange= function () {
if (this.readyState == 'complete') helper();
}
script.onload= helper;
script.src= 'helper.js';
head.appendChild(script);
Check this article for help with implementation without using external libraries
From the variable name activeDictionary If I guess that you are loading some data sets as opposed to javascript programs, you should try looking into JSON and loading and using them dynamically.
If this Question/Answer satisfies your needs, you should delete your question to avoid duplicate entries in SO.
The best way to achieve this would be with jQuery:
$(document).ready(function() {
$('#button').click(function() {
var html = "<script src='newfile.js' type='text/javascript'></script>";
var oldhtml = "<script src='firstSheet.js' type='text/javascript'></script>";
if ($(this).attr('src') == 'firstSheet.js') {
$('script[src="firstSheet.js"]').replace(html);
return;
}
$('script[src="newfile.js"]').replace(oldhtml);
});
});
I would suggest you create the elements how they should be and then append them. Also, if you are dynamically adding the firstSheet.js you shouldn't include it in your .html file.
function thirdLevelQuestions(secondLevelAnswer) {
var mainUserMenu = document.getElementById('mainUserMenu');
activeDictionary = firstKey + secondKey + '.js';
var css = document.createElement('link');
css.rel = 'stylesheet';
css.type = 'text/css';
css.href = 'main.css';
var script1 = document.createElement('script');
script1.type = 'text/javascript';
script1.src = 'firstSheet.js';
var script2 = document.createElement('script');
script2.type = 'text/javascript';
script2.src = activeDictionary;
document.head.appendChild(css);
document.head.appendChild(script1);
document.head.appendChild(script2);
for (var i = 0; i < availableOptions.length; i++) {
var btn = document.createElement('button');
btn.onclick = 'fourthLevelQuestions(' + i + ')';
var val = document.createTextNode(availableOptions[i]);
btn.appendChild(val);
mainUserMenu.appendChild(btn);
}
}
I'm trying to pass a script into an iframe dynamically so it will run there (content in the example comes from the server) using this snippet:
content = '<script type="text/javascript">document.write("bla"");</script>';
el = document.getElementById('iframeName');
iframeDoc = el.contentWindow.document;
tempEl = iframeDoc.createElement('div');
tempEl.innerHTML = content;
It runs great on new browsers but when I try to run it on IE8 and lower, the innerHTML comes up null.
I tried different approaches but the inner HTML is the only option i can think of that can run the script i'm passing in to tempEl. Any ideas on how to pass content into tempEl.innerHTML so it will run the script and also work on IE8-?
Have you tried injecting the script element into the head of the document?
I am not to sure about script tags, but you must inject link and style elements into the head of a document for it to be interpreted correctly by older IE browsers.
var script = document.createElement('script');
script.type = 'text/javascript';
script.rel = 'JavaScript';
script.innerHTML = 'document.write("bla");';
var el = document.getElementById('iframeName');
iframeDoc = el.contentWindow.document;
iframeDoc.head.appendChild(script);
The solution I went with is:
el = document.getElementById('iframeName');
iframeDoc = el.contentWindow.document;
iframeDoc.write(content);
it's a lot shorter and is cross-browser (instead of using innerHTML).
I am loading scripts and style-sheets dynamically from javascript like this.
The problem is that browser does not wait for the script to load.
consider i have a function named functionToBeCalled() inside a script file named script-file.js
i have a function to load script file.
<script type="text/javascript">
var listOfJavaScriptsLoaded = new Array();
function LoadScriptFile(scriptUrl){
var isScriptLoaded = false;
var i = 0;
for(i = 0; i < listOfJavaScriptsLoaded.length; i ++){
if(listOfJavaScriptsLoaded[i] == scriptUrl){
isScriptLoaded = true;
break;
}
}
if(isScriptLoaded == false){
var headTag= document.getElementsByTagName('head')[0];
var scriptTag= document.createElement('script');
scriptTag.type= 'text/javascript';
scriptTag.src= scriptUrl;
headTag.appendChild(scriptTag);
listOfJavaScriptsLoaded.push(scriptUrl);
}
}
LoadScriptFile("script-file.js");
functionToBeCalled();
</script>
now, what happens is that the browser does not wait for the script tag to load and goes to the next command. I get a "undefined functionToBeCalled()" error. this is natural. But the fact is that when i inspect in firebug, the script tag has been formed and the file has loaded.
So how do i make the browser to pause loading and resume after the asset has been loaded?
Edit1: This problem occurs only when i am loading the page in ajax and not in normal page loads
Edit2: Or is there a possibility to read a script/css file from javascript and write it directly in the markup within script tags
If i use window.stop() the loading stops completely. how can i make it resume from the same line?
Or is it possible to make the browser to consider that the loading is still happening and reset it in the onload event?
You may have specific reasons to load the script dynamically, but to present the option, if you write out the script element in your HTML output like so:
<script src="script-file.js"></script>
<script>functionToBeCalled();</script>
the browser will halt parsing until that script has been loaded, and interpreted.
This is also valid in the BODY.
Check out LABjs ( http://labjs.com/ ) by Getify Solutions. LABjs allows script-inserted scripts to be loaded concurrently but run in order.
pretty much every tag which loads a resource has an onload event. so in plain javascript this means in your case something like this:
var s = document.createElement('script');
s.src = "script-file.js";
s.type = 'text/javascript';
head.appendChild(s);
s.onload = function(){
functionToBeCalled();
}
I would recommend looking at Cuzillion. It will allow you to experiment with calling javascript and css in many different ways to see how they react in the browser.
This should answer your question. Just execute it before your page is done loading the body.
<script type="text/javascript">
var loadScriptFile = (function(){
var listOfJavaScriptsLoaded = [];
return function(scriptUrl){
var isScriptLoaded = false;
for(var i = 0; i < listOfJavaScriptsLoaded.length; i ++){
if(listOfJavaScriptsLoaded[i] == scriptUrl){
isScriptLoaded = true;
break;
}
}
if(!isScriptLoaded){
document.write('<scr' + 'ipt type="text/javascript" src="' + scriptUrl + '"></scr' + 'ipt>');
}
};
}());
loadScriptFile("script-file.js");
functionToBeCalled();
</script>