Passing a variable from <script> to HTML code? - javascript

I would like to pass a variable declared between <script> TAGs to the URL in a iframe TAG, like this:
<script>
var1 = 1;
</script>
<iframe src="http://link_to_site?param=var1>
But this does not work. Is it possible to do it and how should i do ?
Thanks

First you would need a way to identify the target element in your JavaScript code. For example, give the element an id attribute:
<iframe id="myIframe" />
(There are certainly other ways to identify an element. Fairly complex queries can be constructed to identify specific elements throughout the DOM. But an id is a particularly straightforward approach at least for this example.)
Then in your JavaScript code you'd get a reference to that element and set its src property:
var var1 = 1;
document.getElementById('myIframe').src = 'http://link_to_site?param=' + var1;
One thing to note is that this code would need to execute after that element exists on the page. If it runs above that element on the page then that element won't yet exist and getElementById() would return nothing.
"After" could mean that it runs immediately but exists "lower" on the page than the element. Or it could mean that the code is placed anywhere on the page but doesn't actually execute until a later time, such as in an event handler.

*FYI- Sorry, but the accepted answer doesn't actually work. Read below. *
I suggest that you use the jQuery JS library to assist in your task. It normalizes the JS environment so you don't have to worry about what browser your user is using.
It also makes it easier to do some simple tasks like getting specific elements etc.
Here is code that shows how to dynamically create an iFrame AFTER the page has finished loading. This is important because JS runs synchronously: in other words, if you write JS for a specific element, but that element doesn't yet exist on the page, the JS will fail.
By wrapping your code in:
$(document).ready(function(){})
you ensure that the page will finishing loading BEFORE your JS executes.
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<body>
</body>
<script type="text/javascript">
var1 = 1;
$(document).ready(function(){
var theiFrame = '<iframe src="http://link_to_site?param=' + var1 + '">';
$('body').append(theiFrame);
})
</script>
iFrames are a little weird in that they mess with the "onload" event of the document. That's another reason to dynamically add your iFrame to the DOM instead of adding it as an HTML tag. Read this article for more info:
http://www.aaronpeters.nl/blog/iframe-loading-techniques-performance

Related

Document.write nondestructive alternative [duplicate]

In tutorials I've learnt to use document.write. Now I understand that by many this is frowned upon. I've tried print(), but then it literally sends it to the printer.
So what are alternatives I should use, and why shouldn't I use document.write? Both w3schools and MDN use document.write.
The reason that your HTML is replaced is because of an evil JavaScript function: document.write().
It is most definitely "bad form." It only works with webpages if you use it on the page load; and if you use it during runtime, it will replace your entire document with the input. And if you're applying it as strict XHTML structure it's not even valid code.
the problem:
document.write writes to the document stream. Calling document.write on a closed (or loaded) document automatically calls document.open which will clear the document.
-- quote from the MDN
document.write() has two henchmen, document.open(), and document.close(). When the HTML document is loading, the document is "open". When the document has finished loading, the document has "closed". Using document.write() at this point will erase your entire (closed) HTML document and replace it with a new (open) document. This means your webpage has erased itself and started writing a new page - from scratch.
I believe document.write() causes the browser to have a performance decrease as well (correct me if I am wrong).
an example:
This example writes output to the HTML document after the page has loaded. Watch document.write()'s evil powers clear the entire document when you press the "exterminate" button:
I am an ordinary HTML page. I am innocent, and purely for informational purposes. Please do not <input type="button" onclick="document.write('This HTML page has been succesfully exterminated.')" value="exterminate"/>
me!
the alternatives:
.innerHTML This is a wonderful alternative, but this attribute has to be attached to the element where you want to put the text.
Example: document.getElementById('output1').innerHTML = 'Some text!';
.createTextNode() is the alternative recommended by the W3C.
Example: var para = document.createElement('p');
para.appendChild(document.createTextNode('Hello, '));
NOTE: This is known to have some performance decreases (slower than .innerHTML). I recommend using .innerHTML instead.
the example with the .innerHTML alternative:
I am an ordinary HTML page.
I am innocent, and purely for informational purposes.
Please do not
<input type="button" onclick="document.getElementById('output1').innerHTML = 'There was an error exterminating this page. Please replace <code>.innerHTML</code> with <code>document.write()</code> to complete extermination.';" value="exterminate"/>
me!
<p id="output1"></p>
Here is code that should replace document.write in-place:
document.write=function(s){
var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length-1];
lastScript.insertAdjacentHTML("beforebegin", s);
}
You can combine insertAdjacentHTML method and document.currentScript property.
The insertAdjacentHTML() method of the Element interface parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position:
'beforebegin': Before the element itself.
'afterbegin': Just inside the element, before its first child.
'beforeend': Just inside the element, after its last child.
'afterend': After the element itself.
The document.currentScript property returns the <script> element whose script is currently being processed. Best position will be beforebegin — new HTML will be inserted before <script> itself. To match document.write's native behavior, one would position the text afterend, but then the nodes from consecutive calls to the function aren't placed in the same order as you called them (like document.write does), but in reverse. The order in which your HTML appears is probably more important than where they're place relative to the <script> tag, hence the use of beforebegin.
document.currentScript.insertAdjacentHTML(
'beforebegin',
'This is a document.write alternative'
)
As a recommended alternative to document.write you could use DOM manipulation to directly query and add node elements to the DOM.
Just dropping a note here to say that, although using document.write is highly frowned upon due to performance concerns (synchronous DOM injection and evaluation), there is also no actual 1:1 alternative if you are using document.write to inject script tags on demand.
There are a lot of great ways to avoid having to do this (e.g. script loaders like RequireJS that manage your dependency chains) but they are more invasive and so are best used throughout the site/application.
I fail to see the problem with document.write. If you are using it before the onload event fires, as you presumably are, to build elements from structured data for instance, it is the appropriate tool to use. There is no performance advantage to using insertAdjacentHTML or explicitly adding nodes to the DOM after it has been built. I just tested it three different ways with an old script I once used to schedule incoming modem calls for a 24/7 service on a bank of 4 modems.
By the time it is finished this script creates over 3000 DOM nodes, mostly table cells. On a 7 year old PC running Firefox on Vista, this little exercise takes less than 2 seconds using document.write from a local 12kb source file and three 1px GIFs which are re-used about 2000 times. The page just pops into existence fully formed, ready to handle events.
Using insertAdjacentHTML is not a direct substitute as the browser closes tags which the script requires remain open, and takes twice as long to ultimately create a mangled page. Writing all the pieces to a string and then passing it to insertAdjacentHTML takes even longer, but at least you get the page as designed. Other options (like manually re-building the DOM one node at a time) are so ridiculous that I'm not even going there.
Sometimes document.write is the thing to use. The fact that it is one of the oldest methods in JavaScript is not a point against it, but a point in its favor - it is highly optimized code which does exactly what it was intended to do and has been doing since its inception.
It's nice to know that there are alternative post-load methods available, but it must be understood that these are intended for a different purpose entirely; namely modifying the DOM after it has been created and memory allocated to it. It is inherently more resource-intensive to use these methods if your script is intended to write the HTML from which the browser creates the DOM in the first place.
Just write it and let the browser and interpreter do the work. That's what they are there for.
PS: I just tested using an onload param in the body tag and even at this point the document is still open and document.write() functions as intended. Also, there is no perceivable performance difference between the various methods in the latest version of Firefox. Of course there is a ton of caching probably going on somewhere in the hardware/software stack, but that's the point really - let the machine do the work. It may make a difference on a cheap smartphone though. Cheers!
The question depends on what you are actually trying to do.
Usually, instead of doing document.write you can use someElement.innerHTML or better, document.createElement with an someElement.appendChild.
You can also consider using a library like jQuery and using the modification functions in there: http://api.jquery.com/category/manipulation/
This is probably the most correct, direct replacement: insertAdjacentHTML.
Try to use getElementById() or getElementsByName() to access a specific element and then to use innerHTML property:
<html>
<body>
<div id="myDiv1"></div>
<div id="myDiv2"></div>
</body>
<script type="text/javascript">
var myDiv1 = document.getElementById("myDiv1");
var myDiv2 = document.getElementById("myDiv2");
myDiv1.innerHTML = "<b>Content of 1st DIV</b>";
myDiv2.innerHTML = "<i>Content of second DIV element</i>";
</script>
</html>
Use
var documentwrite =(value, method="", display="")=>{
switch(display) {
case "block":
var x = document.createElement("p");
break;
case "inline":
var x = document.createElement("span");
break;
default:
var x = document.createElement("p");
}
var t = document.createTextNode(value);
x.appendChild(t);
if(method==""){
document.body.appendChild(x);
}
else{
document.querySelector(method).appendChild(x);
}
}
and call the function based on your requirement as below
documentwrite("My sample text"); //print value inside body
documentwrite("My sample text inside id", "#demoid", "block"); // print value inside id and display block
documentwrite("My sample text inside class", ".democlass","inline"); // print value inside class and and display inline
I'm not sure if this will work exactly, but I thought of
var docwrite = function(doc) {
document.write(doc);
};
This solved the problem with the error messages for me.

getting the contents of a div tag with class

I am trying to read the particular contents of an child IFrame wrapped in a div tag from parent window. I am using
detailsValue = window.frames['myIframe'].document.getElementById('result').innerHTML;
with this I'm able to access the entire content of that frame. But I need to access only a portion of that content. The problem is that the div which wraps the content that I am looking for contains only class and no ID.
<div class="watINeed"> <table class="details"> </table> </div>
I am unable to access the content which is in a form of table (with no id and only class).
Any help.
Edit1: I need to access the content of the table to check for char length and also for some html tags present in that content.
You can do this either using plain Javascript (as mentioned by Notulysses):
window.frames['myIframe'].document.querySelector('.watINeed .details')
or using jQuery (since you aded jquery) by specifying the iframe's document as context to $:
$(".watINeed .details", window.frames['myIframe'].document)
In the latter case you've a fullfeatured jQuery object.
Note that in either case the iframe's document has to be on the same domain otherwise you'd run into cross origin issues.
Tested against jQuery 2.0.x
Update
If you're running the selector during page load of the including page, you'll have to listen to the load event of the iframe before accessing its content:
$(window.frames['myIframe']).on("load", function(){
// above query here
});
If your are looking for a vanilla Javascript, and your target div is a direct children of starting selector, it is a simple task
var detailsValue = window.frames['myIframe'].document.getElementById('result').innerHTML;
var target;
for(var i = 0; i< detailsValue.children.lenght; i ++){
if(detailsValue.children[i].getAttribute('class')== 'watINeed'){
target = detailsValue.children[i] ;
}
}
otherwise, have to write a recursive method to scrap all children of structure
As i wrote above, it can be done using the following:
document.querySelectorAll(".className")[0] or $(".className")[0]
those are basically the same as both return a list of nodes and the [0] simply means taking the first result from the list.
there are 2 things to pay attention to:
the iframe loads the content asynchronously therefore when you execute the query its most likely that the elements you are searching for did not load yet.
executing the code after DOM loads is not enough.
the solution is simply put your code in a block that executes once all the asynchronous content is loaded:
window.onload=function(){
window.frames['myIframe'].document.querySelectorAll(".watINeed")[0];
}
or the jQuery alternative:
$(window).load(function(){
window.frames['myIframe'].document.querySelectorAll(".watINeed")[0];
});
the second thing is, according to the page Here, you can access the iframe's document using contentWindow.document:
window.onload=function(){
window.frames['myIframe'].contentWindow.document.querySelectorAll(".watINeed")[0];
}
or the jQuery alternative:
$(window).load(function(){
window.frames['myIframe'].contentWindow.document.querySelectorAll(".watINeed")[0];
});
live example: Fiddle

Is it the last `script` element the currently running script?

Is it safe to assume that the last script element* in the document when the script runs** is the currently running script?
For example, I want to create a script that can be dropped anywhere in the body of of a page and display an element in the same place. I'm doing something like this:
function getCurrentScriptElement() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
}
var script = getCurrentScriptElement();
var view = document.createElement('span');
/* Put stuff in our view... */
script.parentNode.insertBefore(view, script);
Assuming the script is in the body of the document, is this "safe?" Will the getCurrentScriptElement function always return the running script? If not, how can it be done?
I'd like to do this without tying the script to a specific id attribute or similar, I'd like it to just be positional.
I created an example here that pulls in this script. One answer suggested that other scripts could create a condition where an example like this would break. Is it possible to add other scripts to this example that will break it?
It was suggested that other scripts with defer or async attributes could break this. Can anyone give an example of how such a script might work?
As I understand it, defer means load the DOM first, and then run the script with the defer tag. How would the defer attribute appearing on another script element affect the behavior of getCurrentScriptElement?
async, as I understand it, means start fetching that script and keep parsing the DOM at the same time, don't wait... but when it hits my script it should still stop and wait, right?
I don't see how either one could affect it, can anyone provide an example?
* I'm only interested in external scripts for the purpose of this question.
** Not the last script element in the entire document, but the last script element in the document at the time when it runs. The rest of the document shouldn't be loaded yet, right?
It's not an absolute guarantee no. Check out this JSFiddle: http://jsfiddle.net/jAsek/
<!DOCTYPE html>
<title>Test case</title>
<div>
<p>At the start</p>
<script id="first">
var scr1 = document.createElement("script");
scr1.setAttribute("id", "early");
document.body.appendChild(scr1);
</script>
<p>After the first script</p>
<script id="second">
function getCurrentScriptElement() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
}
alert(getCurrentScriptElement().id);
</script>
<p>At the end</p>
</div>
Here the alert reports the id of the injected script "early", not the id of currently running script "second".
There's no practical difference between internal and external scripts.
I don’t think it’s a safe assumption at all, as browsers execute javascript code quite differently depending on a number of things (like if you have other script elements in the head, if they are external etc.).
You should just require people to use a dummy element with a custom id or class. That way you will also make it possible to do whatever you do multiple times a page without having to run the script multiple times.
This is also what is done when using widgets, for example Google’s +1 button.
An alternative would be to use document.write to write additional content while the script is executed. This will not replace the script tag however, but simply add something after it.
You probably want to use document.currentScript that is currently supported by 90% of browsers and fallback to document.scripts[document.scripts.length-1] if you're targetting IE
function writeHere(element)
{
var sc = document.currentScript || document.scripts[document.scripts.length-1] ;
sc.parentNode.insertBefore(element, sc);
// or in jquery $(sc).before($(element));
}
note: I didn't test document.scripts[document.scripts.length-1] thoroughly but it should work in most cases (but not in Alohci exemple).
And this is a fix for IE so who cares :)

What are alternatives to document.write?

In tutorials I've learnt to use document.write. Now I understand that by many this is frowned upon. I've tried print(), but then it literally sends it to the printer.
So what are alternatives I should use, and why shouldn't I use document.write? Both w3schools and MDN use document.write.
The reason that your HTML is replaced is because of an evil JavaScript function: document.write().
It is most definitely "bad form." It only works with webpages if you use it on the page load; and if you use it during runtime, it will replace your entire document with the input. And if you're applying it as strict XHTML structure it's not even valid code.
the problem:
document.write writes to the document stream. Calling document.write on a closed (or loaded) document automatically calls document.open which will clear the document.
-- quote from the MDN
document.write() has two henchmen, document.open(), and document.close(). When the HTML document is loading, the document is "open". When the document has finished loading, the document has "closed". Using document.write() at this point will erase your entire (closed) HTML document and replace it with a new (open) document. This means your webpage has erased itself and started writing a new page - from scratch.
I believe document.write() causes the browser to have a performance decrease as well (correct me if I am wrong).
an example:
This example writes output to the HTML document after the page has loaded. Watch document.write()'s evil powers clear the entire document when you press the "exterminate" button:
I am an ordinary HTML page. I am innocent, and purely for informational purposes. Please do not <input type="button" onclick="document.write('This HTML page has been succesfully exterminated.')" value="exterminate"/>
me!
the alternatives:
.innerHTML This is a wonderful alternative, but this attribute has to be attached to the element where you want to put the text.
Example: document.getElementById('output1').innerHTML = 'Some text!';
.createTextNode() is the alternative recommended by the W3C.
Example: var para = document.createElement('p');
para.appendChild(document.createTextNode('Hello, '));
NOTE: This is known to have some performance decreases (slower than .innerHTML). I recommend using .innerHTML instead.
the example with the .innerHTML alternative:
I am an ordinary HTML page.
I am innocent, and purely for informational purposes.
Please do not
<input type="button" onclick="document.getElementById('output1').innerHTML = 'There was an error exterminating this page. Please replace <code>.innerHTML</code> with <code>document.write()</code> to complete extermination.';" value="exterminate"/>
me!
<p id="output1"></p>
Here is code that should replace document.write in-place:
document.write=function(s){
var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length-1];
lastScript.insertAdjacentHTML("beforebegin", s);
}
You can combine insertAdjacentHTML method and document.currentScript property.
The insertAdjacentHTML() method of the Element interface parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position:
'beforebegin': Before the element itself.
'afterbegin': Just inside the element, before its first child.
'beforeend': Just inside the element, after its last child.
'afterend': After the element itself.
The document.currentScript property returns the <script> element whose script is currently being processed. Best position will be beforebegin — new HTML will be inserted before <script> itself. To match document.write's native behavior, one would position the text afterend, but then the nodes from consecutive calls to the function aren't placed in the same order as you called them (like document.write does), but in reverse. The order in which your HTML appears is probably more important than where they're place relative to the <script> tag, hence the use of beforebegin.
document.currentScript.insertAdjacentHTML(
'beforebegin',
'This is a document.write alternative'
)
As a recommended alternative to document.write you could use DOM manipulation to directly query and add node elements to the DOM.
Just dropping a note here to say that, although using document.write is highly frowned upon due to performance concerns (synchronous DOM injection and evaluation), there is also no actual 1:1 alternative if you are using document.write to inject script tags on demand.
There are a lot of great ways to avoid having to do this (e.g. script loaders like RequireJS that manage your dependency chains) but they are more invasive and so are best used throughout the site/application.
I fail to see the problem with document.write. If you are using it before the onload event fires, as you presumably are, to build elements from structured data for instance, it is the appropriate tool to use. There is no performance advantage to using insertAdjacentHTML or explicitly adding nodes to the DOM after it has been built. I just tested it three different ways with an old script I once used to schedule incoming modem calls for a 24/7 service on a bank of 4 modems.
By the time it is finished this script creates over 3000 DOM nodes, mostly table cells. On a 7 year old PC running Firefox on Vista, this little exercise takes less than 2 seconds using document.write from a local 12kb source file and three 1px GIFs which are re-used about 2000 times. The page just pops into existence fully formed, ready to handle events.
Using insertAdjacentHTML is not a direct substitute as the browser closes tags which the script requires remain open, and takes twice as long to ultimately create a mangled page. Writing all the pieces to a string and then passing it to insertAdjacentHTML takes even longer, but at least you get the page as designed. Other options (like manually re-building the DOM one node at a time) are so ridiculous that I'm not even going there.
Sometimes document.write is the thing to use. The fact that it is one of the oldest methods in JavaScript is not a point against it, but a point in its favor - it is highly optimized code which does exactly what it was intended to do and has been doing since its inception.
It's nice to know that there are alternative post-load methods available, but it must be understood that these are intended for a different purpose entirely; namely modifying the DOM after it has been created and memory allocated to it. It is inherently more resource-intensive to use these methods if your script is intended to write the HTML from which the browser creates the DOM in the first place.
Just write it and let the browser and interpreter do the work. That's what they are there for.
PS: I just tested using an onload param in the body tag and even at this point the document is still open and document.write() functions as intended. Also, there is no perceivable performance difference between the various methods in the latest version of Firefox. Of course there is a ton of caching probably going on somewhere in the hardware/software stack, but that's the point really - let the machine do the work. It may make a difference on a cheap smartphone though. Cheers!
The question depends on what you are actually trying to do.
Usually, instead of doing document.write you can use someElement.innerHTML or better, document.createElement with an someElement.appendChild.
You can also consider using a library like jQuery and using the modification functions in there: http://api.jquery.com/category/manipulation/
This is probably the most correct, direct replacement: insertAdjacentHTML.
Try to use getElementById() or getElementsByName() to access a specific element and then to use innerHTML property:
<html>
<body>
<div id="myDiv1"></div>
<div id="myDiv2"></div>
</body>
<script type="text/javascript">
var myDiv1 = document.getElementById("myDiv1");
var myDiv2 = document.getElementById("myDiv2");
myDiv1.innerHTML = "<b>Content of 1st DIV</b>";
myDiv2.innerHTML = "<i>Content of second DIV element</i>";
</script>
</html>
Use
var documentwrite =(value, method="", display="")=>{
switch(display) {
case "block":
var x = document.createElement("p");
break;
case "inline":
var x = document.createElement("span");
break;
default:
var x = document.createElement("p");
}
var t = document.createTextNode(value);
x.appendChild(t);
if(method==""){
document.body.appendChild(x);
}
else{
document.querySelector(method).appendChild(x);
}
}
and call the function based on your requirement as below
documentwrite("My sample text"); //print value inside body
documentwrite("My sample text inside id", "#demoid", "block"); // print value inside id and display block
documentwrite("My sample text inside class", ".democlass","inline"); // print value inside class and and display inline
I'm not sure if this will work exactly, but I thought of
var docwrite = function(doc) {
document.write(doc);
};
This solved the problem with the error messages for me.

How can I append a new element in place in JavaScript?

I've created a JavaScript script that can be pasted on someone's page to create an iFrame. I would like for the person to be able to paste the script where they would like the iFrame to appear.
However, I can't figure out how to append the DOM created iFrame to the location where the script has been pasted. It always appends it to the very bottom of the body.
How do I append in place?
Mm. You could do:
document.write("<div id='iframecontainer'></div>");
document.getElementById('iframecontainer').innerHTML = '...';
But that feels ugly/wrong in a lot of different levels. I am not aware of any other alternatives, though.
EDIT: Actually, a quick google search revealed this artlcle which discusses why document.write is ugly and a nice alternative for the particular pickle you're in: Give your script tag an ID!
<script id="iframeinserter" src=".."></script>
And then you can get a reference to the script tag and insert the iframe before it:
var newcontent = document.createElement('iframe');
var scr = document.getElementById('iframeinserter');
scr.parentNode.insertBefore(newcontent, scr);
Paulo's answer can also be done without the ID, by simply looking for the last <script> element. This must be the script block we're in, because JavaScript guarantees all content past the closing tag of the current script block has not yet been parsed(*):
var scripts= document.getElementsByTagName('script');
var script= scripts[scripts.length-1];
script.parentNode.insertBefore(d, script);
This can be put in an onload/ready function if you like, but if so the ‘var script’ must be calculated at include-time and not in the ready function (when that executes, more <script>s will have been parsed).
(*: except in the case of <script defer>.)
If the user can give an id of an element that will be where the iframe should be, then it would be possible to just use css to move the iframe to where it should be on the page.

Categories