How to place dom element in HTML without rendering it? - javascript

I need to place html/javascript code in html page that I don't want to render/run at page load.
I want to get this content later with `innerHTML' and use it dynamical.
How can code be saved in html page so that browsers ignore it but it can be manipulated with Javascript dom interaction.
<script norun="true" type="text/xml">
<script type="text/javascript"></script>
<div>html</div>
</script>
Above code is not working, because closing tag of inner <script> tag closes the parent "text/xml" type script. Need corss-platform method to keep JS/HTML inside dormant and intact.

Future solution: HTML5 <template>. But it's not supported on IE
Safe solution: entity-escape or base64 encode and decode in javascript
Hacky: <svg style="display:none;"><![CDATA[ your inert content here ]]></svg>, since html5 itself does not allow cdata but an exception is made for foreign content, i.e. svg and mathml

When I do a similar thing (usually with view fragments for SPA-style pages) I generally go with the script tag approach similar to what you were doing. This is assuming I'm using Hogan.js but you can ignore those details for the example:
<script type="text/hogan-template" id="tweet-template">
<div class="tweet">
<div class="username">{{username}}</div>
<div class="body">{{body}}</div>
<div class="date-posted">{{posted}}</div>
</div>
</script>
From here you can fetch it via JavaScript when you need it and the browser ignores it as HTML.
var tweetTemplateEl = document.querySelector("#tweet-template"),
tweetTemplate = hogan.compile(tweetTemplateEl.innerHTML);
var tweetList = document.querySelector("#tweet-list"),
tweetItem = document.createElement("li");
tweetItem.innerHTML = tweetTemplate.render(tweetData);
tweetList.appendChild(tweetItem);
For JavaScript, you could use something like a placeholder on the page. I'm not really sure the most semantic tag to use so I'll use div as it's generally a go to "default" tag. So, in place of where you'd place an include you could simply do:
<div class="script-placeholder" id="some_js_file_name" data-href="/js/some_js_file_name.js"></div>
This could be used in multiple ways but here's one option:
window.require = function(fileName) {
var body = document.querySelector("body"),
placeholder = document.querySelector(["#", fileName].join("")),
src = placeholder.dataset.href,
script = document.createElement("script");
script.src = src;
body.appendChild(script);
};
Which you can then add to whatever you'd like to initiate a require:
someElement.addEventListener("click", function(e) {
require("some_js_file_name");
});
That might be something that allows you to achieve what you'd like to achieve.
NOTE: It's important to note that if you really want to go the dynamic load route you should probably invest in an established library to accomplish this like CommonJS if you'd like the style of requires like I've shown here. This handwritten sample is just that, it's a sample to give you an idea of what is going on.

Wrap the code in an HTML comment: <!-- ... -->
Browsers will not parse or render anything inside of the comment.
When you want to initiate rendering, uncomment the code. This can be done by wrapping the comment in an identifiable element, getting its innerHTML, stripping out the comment syntax, and resetting the innerHTML of the element to be the live uncommented markup.
http://jsfiddle.net/92du461p/2/
<button id="b">Show hidden content</button>
<div id="delay-render"><!-- <p>hide me</p> --></div>
<script>
var r = document.getElementById('delay-render');
var b = document.getElementById('b');
b.addEventListener('click', function() {
r.innerHTML = r.innerHTML.match(/^<!--(.*)-->$/)[1]
});
</script>

Related

Insert javascript to a specific div using google tag manager

I currently implement iframe code using Google Tag - Custom HTML to a specific div (id="empty-div") on my target page using the below code and it works just fine. Google Tag manager (Custom Html tag + Trigger)
Custom HTML tag:
<script type="text/javascript">
jQuery('[id="empty-div"]').html('<iframe src="https://feed.mikle.com/widget/v2/115010/" height="1200px" width="100%" class="fw-iframe" scrolling="no" frameborder="0"></iframe>');
</script>
I want to replace the iframe code above with the below provided code.
<script type="text/javascript" src="https://feed.mikle.com/js/fw-loader.js" data-fw-param="115010/"></script>
Looking for a simple implementation
Doing the below causes errors when publishing in Google Tag Manager
<script type="text/javascript">
jQuery('[id="empty-div"]').html('<script type="text/javascript" src="https://feed.mikle.com/js/fw-loader.js" data-fw-param="115010/"></script>');
</script>
I've looked up what caused the error.
Parse error. Unterminated string literal
In Gtag you are adding a script that adds a script. When you upload your code it will become one big line of text. And that text opens with <script> and closes with </script>. In HTML this would all go right, but since it is a string the script tag will get closed off too early because there is another </script> tag in the middle that closes of the first tag. Leaving all code that follows it to be unparseable and will throw an error.
So I suggest that you use this script below which will use vanilla JavaScript to create a new <script> element, set the correct src and puts it inside the <div id="empty-div">. I've chosen to do it without jQuery since I find it easier to explain what happens without it. And it makes no difference for performance or other JavaScript at all.
I don't get the reason why you would append the <script> in a div, since that is not common practice and will not make a difference whatsoever on the <div id="empty-div">, but I'm sure you have your reasons.
<script>
(function () {
var empty = document.getElementById('empty-div'); // Select empty div
var script = document.createElement('script'); // Create new script element
script.setAttribute('data-fw-param', '115010'); // Add the data-fw-param attribute
script.async = true; // Set async to true. This can improve performance
script.src = https://feed.mikle.com/js/fw-loader.js; // Set the src to the script
empty.appendChild(script); // Insert the script into the empty div
}())
</script>
I've found the explanation for the problem in this SO thread. Please for future reference search the error that a browser or tool is giving you. You'll have more chance of finding your answer, and faster!
Hope this helps and happy coding!

How to document.write() within an image src string? Doesn't get parsed

This is for a Javascript application that is only intended to run on a local machine, accessing many large image files from local disk.
Original code like this:
<script>
// Constants, var inits, etc.
</script>
<-- Then html stuff including some control buttons, each one like this -->
<img id="pgb_runStop" onclick="click_runStop()" src="buttons/but_run.png">
<--then a chunk of javascript related to the buttons -->
The thing works OK, see http://everist.org/NobLog/20150424_js_animated_gallery.htm
Now I want to extend it, so all image pathnames are defined as js constants and vars.
Some will remain fixed during lifetime of the browser page, others will change by
user actions.
I'm stuck with one part of this.
How to get the html parser to pay attention to script blocks WITHIN <img .... > statements?
Specifically, I want to do a document.write() within the image src string.
Like: <img src="<script>document.write(B_PATH)</script>something.png">
This is for the initial page display. The images later get changed by scripts, and that's working OK.
But the html parser doesn't seem to notice scripts inside html elements.
I'm a javascript nubie, so I may have some stupid misconception of how it all works.
Am I just doing it wrong, or is this fundamentally impossible due to reasons?
Here's an example:
<script>
// Constants
PGL_BUT_PATH = "buttons/" // where the button images etc are.
</script>
<-- some html stuff -->
<-- including some control buttons, each one like this -->
<img id="pgb_runStop" onclick="click_runStop()"
src="<script>document.write(PGL_BUT_PATH);</script>but_run.png">
<--then a chunk of javascript related to the buttons -->
In debugger, the img element appears as:
<img id="pgb_runStop" onclick="click_runStop()"
src="<script>document.write(PGL_BUT_PATH);</script>but_run.png"/>
The intent was to get this:
<img id="pgb_runStop" onclick="click_runStop()"
src="buttons/but_run.png"/>
I could just give up with trying to have the page initially render with the correct buttons, and have js correct them afterwards. I'm just surprised... Isn't it possible to evaluate js constants during initial html parsing to construct the DOM, in this way?
Edit to add:
Sorry, I wasn't clear enough in the question. What I want is a way for js to make the html content/DOM correct (per js config values that get defined very early on) BEFORE the page first renders. To avoid any flicker or resizings after first render.
So another solution would be to delay the first page render till after some scripts have run, so they can make initial DOM adjustments before the user sees anything. Any way to do that?
Hmmm... actually that would solve another problem I have. I'll try searching for that.
The semantic templating tools suggest are interesting (had never heard of it. http://www.martin-brennan.com/semantic-templates-with-mustache-js-and-handlebars-js/ ) but am I correct that all such scripting add-ons will execute after the page first renders?
You cannot embed a tag within another tag's attribute. So you cannot embed a <script> inside the src of an <img>. That's just invalid won't-be-parsed HTML.
What you can do, though, is write the attribute after the fact:
<img id="uniqueId">
<script>
var img = document.getElementById('uniqueId')
img.setAttribute('src', PGL_BUT_PATH)
</script>
The <img> tag without a src attribute in that is invalid HTML technically, although it will probably work in any browser anyway. But if you want to stay totally legit, create the <img> with JavaScript too.
<div id="uniqueId"></div>
<script>
var elem = document.getElementById('uniqueId');
var img = document.createElement('img');
img.setAttribute('src', PGL_BUT_PATH);
elem.appendChild(img);
</script>
Tthough I really have no idea why would you like to do this.
This one works for me
<img id="pgb_runStop" onclick="click_runStop()"
src = "about:blank"
onerror="javascript:this.src = PGL_BUT_PATH + 'but_run.png'; this.onerror = null;>
or Another way
<script>
function createImg(src) {
document.write("<img src='" + src + "'>");
}
</script>
<script>createImg(PGL_BUT_PATH + 'but_run.png')</script>
Another more generic approach
<script>
function templete(temp, src) {
document.write(temp.replace("$STR", src));
}
</script>
<script>templete('<img id="pgb_runStop" onclick="click_runStop()" src="$STR"/>', PGL_BUT_PATH + 'but_run.png')</script>
Javascript isn't a templating engine in and of itself, and it looks like that's what you're trying to achieve here. Look into a javascript template library such as Handlebars and you'll have more luck.
Unfortunately, JavaScript doesn't work that way you are setting the src to <script></script> which all the browser thinks of it is just a weird URL. Try:
document.getElementById('pgb_runStop').src = PGL_BUT_PATH + 'but_run.png';
You can change pgb_runStop to whatever is the id of the element.
You can use a Framework like Angular.js to do things like that. I don't use angular.js myself but you can of some pretty incredible stuff with it.
Here's a list of even more engines that you can use
You can also use:
document.getElementById('pgb_runStop')setAttribute('src', PGL_BUT_PATH + 'but_run.png');
Basically, you can do:
(function(){window.onload=function(){
document.getElementById('pgb_runStop')setAttribute('src', PGL_BUT_PATH + 'but_run.png');
};}());
Which should function the exact same
Why not write the whole image in:
document.write('<img src="' + PGL_BUT_PATH + 'but_run.png"/>');
Fiddle

How do I get the original innerHTML source without the Javascript generated contents?

Is it possible to get in some way the original HTML source without the changes made by the processed Javascript? For example, if I do:
<div id="test">
<script type="text/javascript">document.write("hello");</script>
</div>
If I do:
alert(document.getElementById('test').innerHTML);
it shows:
<script type="text/javascript">document.write("hello");</script>hello
In simple terms, I would like the alert to show only:
<script type="text/javascript">document.write("hello");</script>
without the final hello (the result of the processed script).
I don't think there's a simple solution to just "grab original source" as it'll have to be something that's supplied by the browser. But, if you are only interested in doing this for a section of the page, then I have a workaround for you.
You can wrap the section of interest inside a "frozen" script:
<script id="frozen" type="text/x-frozen-html">
The type attribute I just made up, but it will force the browser to ignore everything inside it. You then add another script tag (proper javascript this time) immediately after this one - the "thawing" script. This thawing script will get the frozen script by ID, grab the text inside it, and do a document.write to add the actual contents to the page. Whenever you need the original source, it's still captured as text inside the frozen script.
And there you have it. The downside is that I wouldn't use this for the whole page... (SEO, syntax highlighting, performance...) but it's quite acceptable if you have a special requirement on part of a page.
Edit: Here is some sample code. Also, as #FlashXSFX correctly pointed out, any script tags within the frozen script will need to be escaped. So in this simple example, I'll make up a <x-script> tag for this purpose.
<script id="frozen" type="text/x-frozen-html">
<div id="test">
<x-script type="text/javascript">document.write("hello");</x-script>
</div>
</script>
<script type="text/javascript">
// Grab contents of frozen script and replace `x-script` with `script`
function getSource() {
return document.getElementById("frozen")
.innerHTML.replace(/x-script/gi, "script");
}
// Write it to the document so it actually executes
document.write(getSource());
</script>
Now whenever you need the source:
alert(getSource());
See the demo: http://jsbin.com/uyica3/edit
A simple way is to fetch it form the server again. It will be in the cache most probably. Here is my solution using jQuery.get(). It takes the original uri of the page and loads the data with an ajax call:
$.get(document.location.href, function(data,status,jq) {console.log(data);})
This will print the original code without any javascript. It does not do any error handling!
If don't want to use jQuery to fetch the source, consult the answer to this question: How to make an ajax call without jquery?
Could you send an Ajax request to the same page you're currently on and use the result as your original HTML? This is foolproof given the right conditions, since you are literally getting the original HTML document. However, this won't work if the page changes on every request (with dynamic content), or if, for whatever reason, you cannot make a request to that specific page.
Brute force approach
var orig = document.getElementById("test").innerHTML;
alert(orig.replace(/<\/script>[.\n\r]*.*/i,"</script>"));
EDIT:
This could be better
var orig = document.getElementById("test").innerHTML + "<<>>";
alert(orig.replace( /<\/script>[^(<<>>)]+<<>>/i, "<\/script>"));
If you override document.write to add some identifiers at the beginning and end of everything written to the document by the script, you will be able to remove those writes with a regular expression.
Here's what I came up with:
<script type="text/javascript" language="javascript">
var docWrite = document.write;
document.write = myDocWrite;
function myDocWrite(wrt) {
docWrite.apply(document, ['<!--docwrite-->' + wrt + '<!--/docwrite-->']);
}
</script>
Added your example somewhere in the page after the initial script:
<div id="test">
<script type="text/javascript"> document.write("hello");</script>
</div>
Then I used this to alert what was inside:
var regEx = /<!--docwrite-->(.*?)<!--\/docwrite-->/gm;
alert(document.getElementById('test').innerHTML.replace(regEx, ''));
If you want the pristine document, you'll need to fetch it again. There's no way around that. If it weren't for the document.write() (or similar code that would run during the load process) you could load the original document's innerHTML into memory on load/domready, before you modify it.
I can't think of a solution that would work the way you're asking. The only code that Javascript has access to is via the DOM, which only contains the result after the page has been processed.
The closest I can think of to achieve what you want is to use Ajax to download a fresh copy of the raw HTML for your page into a Javascript string, at which point since it's a string you can do whatever you like with it, including displaying it in an alert box.
A tricky way is using <style> tag for template. So that you do not need rename x-script any more.
console.log(document.getElementById('test').innerHTML);
<style id="test" type="text/html+template">
<script type="text/javascript">document.write("hello");</script>
</style>
But I do not like this ugly solution.
I think you want to traverse the DOM nodes:
var childNodes = document.getElementById('test').childNodes, i, output = [];
for (i = 0; i < childNodes.length; i++)
if (childNodes[i].nodeName == "SCRIPT")
output.push(childNodes[i].innerHTML);
return output.join('');

Getting content of a script file using Javascript

I have the following script element in my web page:
<script src="default.js" type="text/javascript"></script>
Using JavaScript, I want to be able to retrieve the content of the script file. I know I could use an ajax request to get the data but then I am getting something from the server that I already have locally.
So what I would prefer to do is retrieve the content from the DOM (if that's possible) or something that has the same result.
Cheers
Anthony
UPDATE
I was trying to simplify the question, maybe a bad a idea, I thought this way would cause less questions.
The real situation I have is as follows, I actually have
<script type="text/html" class="jq-ItemTemplate_Approval">
...
html template that is going to be consumed by jQuery and jTemplate
...
</script>
Now this works fine but it means each time the page loads I have to send down the template as part of the HTML of the main page. So my plan was to do the following:
<script src="template.html" type="text/html"></script>
This would mean that the browser would cache the content of template.html and I would not have to send it down each time. But to do this I need to be able to get the content from the file.
Also in this case, as far as I know, requesting the content via ajax isn't going to help all that much because it has to go back to the server to get the content anyway.
If I understand you correctly, you don't want to use Ajax to load an html template text, but rather have it loaded with the rest of the page. If you control the server side, you can always include the template text in an invisible div tag that you then reference from Javascript:
<div id="template" style="display:none;">
...template text...
</div>
<script>
// pops up the template text.
alert(document.getElementById("template").innerHTML);
</script>
If you are just looking for to load the template so that you can have it cached, you can put the contents in a variable like this:
<script>
var template = "template text..";
</script>
or you can load it using ajax and store the template in a variable so it is accessible. It's pretty trivial in jquery:
var template;
$.get("template.html", function(data){
template = data;
});
unless you load a script as literal text in the page, it does not exist as text. It is interpreted by the browser and melded into the runtime, with any other scripts.
If you want the source you have to fetch it again,if with Ajax get the responseText.
It will come from the browser cache, and doesn't have to be downloaded again.
I think what you want to do is to assign a variable inside template.js. Then you have the variable available for use wherever you want in jquery. Something like:
var tpl = "<div> ... </div>"
Wouldn't this be a simpler solution to your problem? We do this in Ext JS. I think this will work for you in jQuery.
You could get the attribute of the src of the script and then use XHR to get the contents of the JS file. It's a much cleaner way of doing it IMO. e.g.:-
if(window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.status == 200 && xhr.readyState == 4) {
var sourceCode = xhr.responseText;
alert('The source code is:-\n'+sourceCode);
}
}
xhr.open("GET",document.getElementById('scriptID').src,true);
xhr.send(null);
}
Using an iFrame & HTML5 Local Storage
Save the templates for rendering later...
not stoked about the iFrame, but it seems to be working pretty good (haven't ran performance tests yet)
Put the iFrame on the page you want the template on (index.html)
<html>
<head>
<iframe src="mustache.Users.html" onload="this.remove();" class="hidden" id="users_template"></iframe>
</head>
</html>
Make sure the src attribute is set
hide the element until you can get rid of it after it loads
Put this body wrapper around your template (mustache.Users.html)
(don't worry it won't show up in the template)
<body onload="localStorage.setItem('users_template',this.document.body.innerHTML);">
<ul class="list-group" id="users" >
{{#users}}<li>{{name}}</li>{{/users}}
</ul>
</body>
replace 'users_template' with whatever name for your variable
the 'onload' attribute saves the template into localStorage during load
Now You can access your templates from anywhere
localStorage.getItem('users_template')
OR
window.localStorage.getItem('users_template')
What is in the JavaScript file? If it's actual code, you can run functions and reference variables in there just like you had cut and paste them into the webpage. You'll want to put the include line above any script blocks that reference it.
Is this what your looking to accomplish?
Why not use Ajax (well Ajah because its html :-))?
when the server is set up correctly and no no-cache or past expires headers are sent, the browser will cache it.
The way that most JavaScript import files work is they include a script, that immediately calls a function with a parameter of certain text, or of another function. To better illustrate, say you have your main index.html file, set it up like this:
<html>
<head>
</head>
<body>
<script>
let modules = {};
function started(moduleName, srcTxt) {
modules[moduleName] = (srcTxt) //or something similar
}
</script>
<!--now you can include other script tags, and any script tags that will be included, their source can be gotten (if set up right, see later)-->
<script src="someOtherFile.js"></script>
</body>
</html>
now make that other file, someOtherFile.js, and right away when its loaded, simply call that "started" function which should already be declared in the scope, and when thats done, then whatever text is passed, from the file, is stored in the main index.html file. You can even stringify an entire function and put it in, for example:
started("superModule", (function() {
/*
<?myCustomTemplateLanguage
<div>
{something}Entire Javascript / html template file goes here!!{/something}
</div>
?>
*/
}).toString());
now you can access the inner content of the function, and get all the text in between the comments, or better yet, then do other parsing etc, or make some other kind of parsing identifiers at the beginning and end of the comments, as shown above, and get all text in between those

Dynamically inserting javascript into HTML that uses document.write

I am currently loading a lightbox style popup that loads it's HTML from an XHR call. This content is then displayed in a 'modal' popup using element.innerHTML = content This works like a charm.
In another section of this website I use a Flickr 'badge' (http://www.elliotswan.com/2006/08/06/custom-flickr-badge-api-documentation/) to load flickr images dynamically. This is done including a script tag that loads a flickr javascript, which in turn does some document.write statments.
Both of them work perfectly when included in the HTML. Only when loading the flickr badge code inside the lightbox, no content is rendered at all. It seems that using innerHTML to write document.write statements is taking it a step too far, but I cannot find any clue in the javascript implementations (FF2&3, IE6&7) of this behavior.
Can anyone clarify if this should or shouldn't work? Thanks.
In general, script tags aren't executed when using innerHTML. In your case, this is good, because the document.write call would wipe out everything that's already in the page. However, that leaves you without whatever HTML document.write was supposed to add.
jQuery's HTML manipulation methods will execute scripts in HTML for you, the trick is then capturing the calls to document.write and getting the HTML in the proper place. If it's simple enough, then something like this will do:
var content = '';
document.write = function(s) {
content += s;
};
// execute the script
$('#foo').html(markupWithScriptInIt);
$('#foo .whereverTheDocumentWriteContentGoes').html(content);
It gets complicated though. If the script is on another domain, it will be loaded asynchronously, so you'll have to wait until it's done to get the content. Also, what if it just writes the HTML into the middle of the fragment without a wrapper element that you can easily select? writeCapture.js (full disclosure: I wrote it) handles all of these problems. I'd recommend just using it, but at the very least you can look at the code to see how it handles everything.
EDIT: Here is a page demonstrating what sounds like the effect you want.
I created a simple test page that illustrates the problem:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Document Write Testcase</title>
</head>
<body>
<div id="container">
</div>
<div id="container2">
</div>
<script>
// This doesn't work!
var container = document.getElementById('container');
container.innerHTML = "<script type='text/javascript'>alert('foo');document.write('bar');<\/script>";
// This does!
var container2 = document.getElementById('container2');
var script = document.createElement("script");
script.type = 'text/javascript';
script.innerHTML = "alert('bar');document.write('foo');";
container.appendChild(script);
</script>
</body>
</html>
This page alerts 'bar' and prints 'foo', while I expected it to also alert 'foo' and print 'bar'. But, unfortunately, since the script tag is part of a larger HTML page, I cannot single out that tag and append it like the example above. Well, I can, but that would require scanning innerHTML content for script tags, and replacing them in the string by placeholders, and then inserting them using the DOM. Sounds not that trivial.
Use document.writeln(content); instead of document.write(content).
However, the better method is using the concatenation of innerHTML, like this:
element.innerHTML += content;
The element.innerHTML = content; method will replace the old content with the new one, which will overwrite your element's innerHTML!
Whereas using the the += operator in element.innerHTML += content will append your text after the old content. (similar to what document.write does.)
document.write is about as deprecated as they come. Thanks to the wonders of JavaScript, though, you can just assign your own function to the write method of the document object which uses innerHTML on an element of your choosing to append the supplied content.
Can I get some clarification first to make sure I get the problem?
document.write calls will add content to the markup at the point in the markup at which they occur. For example if you include document.write calls in a function but call the function elsewhere, the document.write output will happen at the point in the markup the function is defined not where it is called.
Therefore for this to work at all the Flickr document.write statements will need to be part of the content in element.innerHTML = content. Is this definitely the case?
You might quickly test if this should work at all by adding a single and simple document.write call in the content that is set as the innerHTML and see what this does:
<script>
var content = "<p>1st para</p><script>document.write('<p>2nd para</p>');</script>"
element.innerHTML = content;
</script>
If that works, the concept of document.write working in content set as the innerHTML of an element might just work.
My gut feeling is that it won't work, but it should be pretty straightforward to test the concept.
So you're using a DOM method to create a script element and append that to an existing element and this then causes the content of the appended script element to execute? That sounds good.
You say that the script tag is part of a larger HTML page and therefore cannot be singled out. Can you not give the script tag an ID and target it? I'm probably missing something obvious here.
In theory, yes, I can single out a script tag that way. The problem is that we potentially have dozens of situations where this occurs, so I am trying to find some cause or documentation of this behavior.
Also, the script tag does not seem to be a part of the DOM anymore after it gets loaded. In our environment, my container div remains empty, so I cannot fetch the script tag. It should work, though, because in my example above the script does not get executed, but is still part of the DOM.

Categories

Resources