Edited qestion
I'm trying to create two separate HTML documents: main.html and sufler.html . Idea is to control sufler.html page from main.html . To do that I found a solution
to write sufler's.html code like string element on main.html page, change what I need in that string element and write it with document.write function from main.html . Everything
works fine except <script> function...
main.html
<script type="text/javascript">
var HTMLstringPage1 = '<!DOCTYPE html><html><head><link href="stilius.css" rel="stylesheet" type="text/css" />',
HTMLstringPage2 = '</body></html>',
HTMLstringStyle = '\x3Cscript type="text/javascript">function styluss(){document.getElementById("flip").style.font="normal normal 30px sans-serif";}\x3C/script>',
HTMLstringDiv1 = '</head><body onload="styluss()"><div id="sufler"><div id="mov"><p id="flip">',
HTMLstringDiv2 = '</p></div></div>';
var newWindow = window.open('sufler.html','_blank','toolbar=no, scrollbars=no, resizable=no, height=615,width=815');
newWindow.document.body.innerHTML = '';
newWindow.document.write(HTMLstringPage1,HTMLstringDiv1+"Text"+HTMLstringDiv2,HTMLstringPage2); //works fine
// newWindow.document.write(HTMLstringPage1,HTMLstringStyle,HTMLstringDiv1+"Text"+HTMLstringDiv2,HTMLstringPage2);//works except script function
</script>
Can someone help on this?
To dynamically add a script tag, it is much better to do this:
var newDoc = newWindow.document;
var script = newDoc.createElement("script");
script.type = "text/javascript";
var text = newDoc.createTextNode('function styluss(){document.getElementById("flip").style.font="normal normal 30px sans-serif";}');
script.appendChild(text);
newDoc.getElementsByTagName("head")[0].appendChild(script);
Working demo: http://jsfiddle.net/jfriend00/JqW5F/
But, creating code on the fly like this is almost never needed. Since you must, by definition, generally already know what you want the code to do, you can just have a pre-created function that takes some arguments and then call that existing function with the right arguments to accomplish what you wanted to rather than creating a custom function on the fly:
function styleIt(id, fontStyle) {
document.getElementById(id).style.font = fontStyle;
}
styleIt("flip", "normal normal 30px sans-serif");
styleIt("flip2", "normal normal 12px sans-serif");
You have to use normal opening brackets for <script> tags:
var HTMLstringStyle = '<script type="text/javascript">function styluss(){document.getElementById("flip").style.font="normal normal 30px sans-serif";}<\/script>';
Although I can't see why you would ever use this... attaching a <script> tag to the <head> or the <body> is almost always a superior solution.
Related
I'm wondering if there is a way to get a handle on the DOM element that contains the script inside it. So if I had:
<script type="text/javascript> var x = ?; </script>
Is there a way that I can assign "x" a reference to the script element that contains "x"?
There isn't a truly safe way.
The closest you can come is to use getElementsByTagName to get all the scripts, get its length, get the last script element, then work from there.
This can fail if the script is deferred or if the script has been dynamically added to the page before another script element.
You could include some marker text in the script element, and then (similar to what David said), you can loop through all the script elements in the DOM (using getElementsByTagName or whatever features your library, if you're using one, may have). That should find the element reliably. That would look something like this (live example):
<body>
<script id='first' type='text/javascript'>
(function() {
var x = "MARKER:first";
})();
</script>
<script id='second' type='text/javascript'>
(function() {
var x = "MARKER:second";
})();
</script>
<script id='third' type='text/javascript'>
(function() {
var x = "MARKER:third";
})();
</script>
<script id='last' type='text/javascript'>
(function() {
var scripts, index, script;
scripts = document.getElementsByTagName("script");
for (index = 0; index < scripts.length; ++index) {
script = scripts[index];
if (script.innerHTML.indexOf("MARKER:second") >= 0
&& script.id !== "last") {
display("Found MARKER:second in script tag #" + script.id);
}
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})();
</script>
</body>
Note that, like the script above, if you're looking for a script tag marker from within a different script tag, you'll need to handle that. Above it's handled by checking the ID of the script tag, but you can also just break it up in the one you don't want to find, like this (live example):
if (script.innerHTML.indexOf("MARKER:" + "second") >= 0) {
display("Found MARKER:" + "second in script tag #" + script.id);
}
I'm trying this with no success. For reference, the bootstrap slider is here : http://seiyria.github.io/bootstrap-slider/.
I'm not a javascript expert, either, so this might be very simple. The bootstrap-slider site has many examples of how to configure the sliders the way you want them. I'm going to have many sliders generated depending on how many objects are pulled from a JSON file or some other data storing method. It could be 2 or it could be 20.
I created a javascript function called createSlider that I've attempted to pass all of the information required at the bootstrap-slider site. I'm not getting any errors in my Chrome debugging area, but nothing is happening. All of the appropriate client-side sources are loading.
function createSlider (orgId) {
slidersList = document.getElementById('slidersList');
element = slidersList.createElement("div");
var sliderElement = element.createElement('input');
var sliderUnique= orgId.concat("Slider");
var sliderUniqueVal = orgId.concat("SliderVal");
sliderElement.setAttribute('id', charityId);
sliderElement.setAttribute('data-slider-id', sliderUnique);
sliderElement.setAttribute('type', 'text');
sliderElement.setAttribute('data-slider-min', '0');
sliderElement.setAttribute('data-slider-max', '100');
sliderElement.setAttribute('data-slider-step', '1');
sliderElement.setAttribute('data-slider-value', '50');
var span = element.createElement('span');
span.setAttribute('style', 'padding-left:5px;');
span.innerHTML =' ';
var innerSpan = span.createElement('span');
innerSpan.setAttribute('id', sliderUniqueVal);
innerSpan.innerHTML = '50';
sliderElement.slider({tooltip: 'hide'});
sliderElement.on("slide", function(slideEvt) {
innerSpan.innerHTML = text(slideEvt.value);
});
}
The slider() function is from the external site, and runs fine if I explicitly call it like the examples state to. Anyone know what's going wrong? Is there a better way to do this? Any ideas would be appreciated.
Note, in plain JavaScript, you can only use document.createElement and then append to another HTML element. You cannot call createElement directly against another HTML element.
I changed some of what you wrote from plain old JavaScript into JQuery, and now seems to work:
P.S. Didn't know where charityId came from, so just added it as another parameter into the function.
$(function() {
createSlider('o1','c1');
createSlider('o2','c2');
createSlider('o3','c3');
});
function createSlider (orgId, charityId) {
var slidersList = $('#slidersList');
var element = $("<div></div>").appendTo(slidersList);
var sliderElement = $("<input/>").appendTo(element);
var sliderUnique= orgId.concat("Slider");
var sliderUniqueVal = orgId.concat("SliderVal");
sliderElement.attr('id', charityId);
sliderElement.attr('data-slider-id', sliderUnique);
sliderElement.attr('type', 'text');
sliderElement.attr('data-slider-min', '0');
sliderElement.attr('data-slider-max', '100');
sliderElement.attr('data-slider-step', '1');
sliderElement.attr('data-slider-value', '50');
var span = $('<span></span>').appendTo(element);
span.attr('style', 'padding-left:5px;');
span.html(' ');
var innerSpan = $('<span></span>').appendTo(span);
innerSpan.attr('id', sliderUniqueVal);
innerSpan.html('50');
sliderElement.slider({tooltip: 'hide'});
sliderElement.on("slide", function(slideEvt) {
innerSpan.text(slideEvt.value);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script type='text/javascript' src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://seiyria.github.io/bootstrap-slider/stylesheets/bootstrap-slider.css">
<script type='text/javascript' src="http://seiyria.github.io/bootstrap-slider/javascripts/bootstrap-slider.js"></script>
<div id="slidersList"></div>
I need to pass some text from the current page to a popup window without going for a server hit. The information (herewith represented by 90) is already available in the parent form (it's like a paragraph-long text which is stored in a hidden variable). I just need to display that as a popup.
Here's what I've tried, this works to some extent but doesn't work if I pass text, instead of a number. My second concern is that the solution kinda looks ugly. Any tips? Thank you.
This is SCCE, you can run it straight in your machine.
<html>
<head>
<title>A New Window</title>
<script type="text/javascript">
var newWindow;
var data;
function makeNewWindow(param) {
data = param;
if (!newWindow || newWindow.closed) {
newWindow = window.open("","sub","status,height=200,width=300");
setTimeout("writeToWindow()", 50); /* wait a bit to give time for the window to be created */
} else if (newWindow.focus) {
newWindow.focus( ); /* means window is already open*/
}
}
function writeToWindow() {
var k = data;
alert(data);
var newContent = "<html><head><title>Additional Info</title></head>";
newContent += "<body><h1>Some Additional Info</h1>";
newContent += "<scr"+"ipt type='text/javascript' language='javascript'> var localVar; localVar = "+ k +"; document.write('localVar value: '+localVar);</scr"+"ipt>";
newContent += "</body></html>";
// write HTML to new window document
newWindow.document.write(newContent);
newWindow.document.close( ); // close layout stream
}
</script>
</head>
<body>
<form>
<input type="button" value="Create New Window" onclick="makeNewWindow('90');" />
</form>
</body>
</html>
Actually, I googled and saw some other approach that uses window.opener.document.forms.element, but here, the window has to know in advance what it has to read from the parent. I need to be able to pass it as it will vary:
<textarea rows="15" name="projectcontent" id="projectcontent" cols="87"></textarea>
<b>View Content</b>
<head>
<title>View Project Content</title>
</head>
<body>
<img src="/images/toplogo.jpg"><br/>
<script language="Javascript">
document.write(window.opener.document.forms['yourformname'].elements['projectcontent'].value)
</script>
<img src="/images/bottomlogo.jpg">
</body>
</html>
use window.opener
From Mozilla Developer Network:
When a window is opened from another window, it maintains a reference
to that first window as window.opener. If the current window has no
opener, this method returns NULL.
https://developer.mozilla.org/en-US/docs/Web/API/window.opener
This way you can have on your original window a callback, and you can notify the window it's load and ready, rather than wait a random delay...
you add a function on the original window:
window.popupReady = function (callbackToPopup) {
callbackToPopup(newData);
}
then the popup can tell the parent window it's ready and pass it a callback to update it with data..
and on the popup try something like:
window.dataReady(newData)
{
alert(newData);
}
document.addEventListener("load", function() { window.opener.popupReady (dataReady); }
I didn't test this code, but I would take such a path as this should ensure the popupWindow is ready for you and is along the spirit of JavaScript.
In your onclick attribute you pass '90' to the function, but the function isn't set up to take an argument. So, change the first line of your function like this:
function writeToWindow(data) {
You don't need the global var data; or the local var k = data;, so get rid of them.
And instead of + k + write + data +.
That should do get your data passed.
Use this code, it works perfectly in all browsers .
#desc = parent text area id
#desc_textarea = popup
$("#desc_textarea").val(window.opener.$("#desc").val())
I'm having some trouble trying to get a fairly simple popupper to work. The idea is that the parent should open a popup window and then append a div in it.
The relevant parts of the code:
parent.html:
var childWindow;
function togglePref() {
childWindow = window.open("popup.html", "prefPopup", "width=200,height=320");
}
function loadPopupElements() {
var prefElements = document.getElementById("prefBrd").cloneNode(true);
var childDoc = childWindow.document;
var childLink = document.createElement("link");
childLink.setAttribute("href", "pop.css");
childLink.setAttribute("rel", "stylesheet");
childLink.setAttribute("type", "text/css");
childDoc.head.appendChild(childLink);
childDoc.body.appendChild(prefElements);
}
popup.html:
<head>
</head>
<body onload="opener.loadPopupElements();">
</body>
This works fine with Safari and Chrome, but for some reason IE refuses to append anything.
Ok, I managed to work around the problem with a uglyish solution using innerHTML. Apparently, as Hemlock mentioned, IE doesn't support appending children from a another document. Some suggested to take a look at the importNode() method but I seemed to have no luck with it either.
So, the workaround goes as follows:
parent.html:
var childWindow;
function togglePref() {
childWindow = window.open("popup.html", "prefPopup", "width=200,height=320");
}
function loadPopupElements() {
var prefElements = document.getElementById("prefBrd");
var childDoc = childWindow.document;
childDoc.body.innerHTML = prefElements.innerHTML;
}
popup.html:
<head>
<link href="pop.css" rel="stylesheet" type="text/css">
</head>
<body onload="loadElements();">
</body>
<script type="text/javascript">
function loadElements() {
opener.loadPopupElements();
}
</script>
This seems quite a nasty way to go because in my case the #prefBrd contains some input elements with dynamically set values, so in order for the popup.html to grab them, it has to do a bit of iteration at the end of the loadElements() function, which wouldn't have been necessary using appendChild.
I'm working on a web page where I'm making an AJAX call that returns a chunk of HTML like:
<div>
<!-- some html -->
<script type="text/javascript">
/** some javascript */
</script>
</div>
I'm inserting the whole thing into the DOM, but the JavaScript isn't being run. Is there a way to run it?
Some details: I can't control what's in the script block (so I can't change it to a function that could be called), I just need the whole block to be executed. I can't call eval on the response because the JavaScript is within a larger block of HTML. I could do some kind of regex to separate out the JavaScript and then call eval on it, but that's pretty yucky. Anyone know a better way?
Script added by setting the innerHTML property of an element doesn't get executed. Try creating a new div, setting its innerHTML, then adding this new div to the DOM. For example:
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var str = "<script>alert('i am here');<\/script>";
var newdiv = document.createElement('div');
newdiv.innerHTML = str;
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
You don't have to use regex if you are using the response to fill a div or something. You can use getElementsByTagName.
div.innerHTML = response;
var scripts = div.getElementsByTagName('script');
for (var ix = 0; ix < scripts.length; ix++) {
eval(scripts[ix].text);
}
While the accepted answer from #Ed. does not work on current versions of Firefox, Google Chrome or Safari browsers I managed to adept his example in order to invoke dynamically added scripts.
The necessary changes are only in the way scripts are added to DOM. Instead of adding it as innerHTML the trick was to create a new script element and add the actual script content as innerHTML to the created element and then append the script element to the actual target.
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var newdiv = document.createElement('div');
var p = document.createElement('p');
p.innerHTML = "Dynamically added text";
newdiv.appendChild(p);
var script = document.createElement('script');
script.innerHTML = "alert('i am here');";
newdiv.appendChild(script);
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
This works for me on Firefox 42, Google Chrome 48 and Safari 9.0.3
An alternative is to not just dump the return from the Ajax call into the DOM using InnerHTML.
You can insert each node dynamically, and then the script will run.
Otherwise, the browser just assumes you are inserting a text node, and ignores the scripts.
Using Eval is rather evil, because it requires another instance of the Javascript VM to be fired up and JIT the passed string.
The best method would probably be to identify and eval the contents of the script block directly via the DOM.
I would be careful though.. if you are implementing this to overcome a limitation of some off site call you are opening up a security hole.
Whatever you implement could be exploited for XSS.
You can use one of the popular Ajax libraries that do this for you natively. I like Prototype. You can just add evalScripts:true as part of your Ajax call and it happens automagically.
For those who like to live dangerously:
// This is the HTML with script element(s) we want to inject
var newHtml = '<b>After!</b>\r\n<' +
'script>\r\nchangeColorEverySecond();\r\n</' +
'script>';
// Here, we separate the script tags from the non-script HTML
var parts = separateScriptElementsFromHtml(newHtml);
function separateScriptElementsFromHtml(fullHtmlString) {
var inner = [], outer = [], m;
while (m = /<script>([^<]*)<\/script>/gi.exec(fullHtmlString)) {
outer.push(fullHtmlString.substr(0, m.index));
inner.push(m[1]);
fullHtmlString = fullHtmlString.substr(m.index + m[0].length);
}
outer.push(fullHtmlString);
return {
html: outer.join('\r\n'),
js: inner.join('\r\n')
};
}
// In 2 seconds, inject the new HTML, and run the JS
setTimeout(function(){
document.getElementsByTagName('P')[0].innerHTML = parts.html;
eval(parts.js);
}, 2000);
// This is the function inside the script tag
function changeColorEverySecond() {
document.getElementsByTagName('p')[0].style.color = getRandomColor();
setTimeout(changeColorEverySecond, 1000);
}
// Here is a fun fun function copied from:
// https://stackoverflow.com/a/1484514/2413712
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
<p>Before</p>