I am in the process of making an addon for a software that basically allows you to have 'responsive' adverts, by checking the page size with javascript and then outputting the relevant ad code to the screen.
This works fine for text ad codes, however I've hit a snag when the ad code is javascript - I can't get the user-provided javascript to output to the page and be executed.
Is this possible at all?
Here is some example code:
<div id="admanagerresponsive">
<script type="text/javascript">
adUnit = document.getElementById("admanagerresponsive");
adWidth = adUnit.offsetWidth;
if (adWidth >= 728) {
<output ad code>
}
</script>
</div>
The code above will be directly in the page.
Is such a thing possible?
EDIT:
could be any advertiser's code, such as adsense. It'll be user provided, and will be standard html. However, it could contain tags, and these will need to be rendered and outputted correctly...
If you really need to inject add html code containing script tags and you are award of the security problems, i suggest to use a library like jQuery that takes care about the cross browser issues with executing <script> tags added later.
Additionally you need to take care about various pitfalls like:
Html paring is done before script parsing, so no matter where a </script> appears this will immediately end your script.
The examples are important for the situations where you have that code as inline script inside your html page.
Example 1:
<script>
adUnit = document.getElementById("admanagerresponsive");
adWidth = adUnit.offsetWidth;if (adWidth >= 728) {
// if you add </script> <b>this is visible as html</b> and everything below is not script anymore
}
</script>
Example 2:
<script>
adUnit = document.getElementById("admanagerresponsive");
adWidth = adUnit.offsetWidth;if (adWidth >= 728) {
var string = "<script> var test;</script>";//the same problem here everything behind the closing script tag is html and not script anymore
}
</script>
So if you need to have some script to inject there you need to make the </script> not to be detectable by the html parser:
<script>
adUnit = document.getElementById("admanagerresponsive");
adWidth = adUnit.offsetWidth;if (adWidth >= 728) {
var string = "<script> var test;</sc"+"ript>";//that way the html parser does not detect the closing script tag
}
</script>
A better solution is not to use inline script at all, not only for that reason, but because you should always keep css, js and html separated.
Break it into two ideas. From your HTML above, just call a js function you wrote somewhere else. Initially have that js function be an alert, to verify that works.
Once that works, you have the problem: how can I get custom js for a page? The answer to that is hopefully that you can create and load a (one-off, custom) js file the same way you create an html file. Or, libraries such as now.js could help. Or, there is a script portion of your html page that you understand how to assemble to include the js.
You could even preload all your size possibilities, then have the js routine from the first paragraph pick the right routine to call,
Related
I've been given some great tips on how to inject HTML into HTML that I can't edit.
The trouble is now that the snippet contains JS it won't render to the page.
The Jquery looks lke this:
$(document).ready(function() {
var $body = $(document.body);
if ($body.is(".ly_productdetails.ProductDetails.en.en_GB")) {
$('.info_section').prepend('<div id="test-widget"></div><script type="text/javascript" src="/frontend/latest/build.min.js" data-test="test-widget" data-instance="xyz" data-apikey="12345678" data-tags="" async="async"></script>');
}
});
I tried putting backslashes in before the quotations but this didn't work.
How else can you write this to the page so that the JS is included?
Many thanks,
Adam
JSfiddle : https://jsfiddle.net/fs6qgzrj/
This is a security feature. jQuery allows <script> elements in HTML code but it won't execute them (at least not the src="..." part; inline scripts work). This is because jQuery has no way to make sure the script isn't malicious or from a safe source (an error in your code might allow people to enter scripts in a form element).
Use jQuery.getScript(url, successCallack) instead.
See also: jQuery - script tags in the HTML are parsed out by jQuery and not executed
It looks like jQuery won't load an external script when parsing HTML. But if you create a script element it will:
$('body').prepend($('<script>', {
src: 'http://dev.bridgebase.com/barmar_test/test.js'
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></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 :)
I'm using varnish+esi to return external json content from a RESTFul API.
This technique allows me to manage request and refresh data without using webserver resources for each request.
e.g:
<head>
....
<script>
var data = <esi:include src='apiurl/data'>;
</script>
...
After include the esi varnish will return:
var data = {attr:1, attr2:'martin'};
This works fine, but if the API returns an error, this technique will generate a parse error.
var data = <html><head><script>...api js here...</script></head><body><h1 ... api html ....
I solved this problem using a hidden div to parse and catch the error:
...
<b id=esi-data style=display:none;><esi:include src='apiurl/data'></b>
<script>
try{
var data = $.parseJSON($('#esi-data').html());
}catch{ alert('manage the error here');}
....
I've also tried using a script type text/esi, but the browser renders the html inside the script tag (wtf), e.g:
<script id=esi-data type='text/esi'><esi:include src='apiurl/data'></script>
Question:
Is there any why to wrap the tag and avoid the browser parse it ?
Let me expand upon the iframe suggestion I made in my comment—it's not quite what you think!
The approach is almost exactly the same as what you're doing already, but instead of using a normal HTML element like a div, you use an iframe.
<iframe id="esi-data" src="about:blank"><esi:include src="apiurl/data"></iframe>
var $iframe = $('#esi-data');
try {
var data = $.parseJSON($iframe.html());
} catch (e) { ... }
$iframe.remove();
#esi-data { display: none; }
How is this any different from your solution? Two ways:
The data/error page are truly hidden from your visitors. An iframe has an embedded content model, meaning that any content within the <iframe>…</iframe> tags gets completely replaced in the DOM—but you can still retrieve the original content using innerHTML.
It's valid HTML5… sort-of. In HTML5, markup inside iframe elements is treated as text. Sure, you're meant to be able to parse it as a fragment, and it's meant to contain only phrasing content (and no script elements!), but it's essentially just treated as text by the validator—and by browsers.
Scripts from the error page won't run. The content gets parsed as text and replaced in the DOM with another document—no chance for any script elements to be processed.
Take a look at it in action. If you comment out the line where I remove the iframe element and inspect the DOM, you can confirm that the HTML content is being replaced with an empty document. Also note that the embedded script tag never runs.
Important: this approach could still break if the third party added an iframe element into their error page for some reason. Unlikely as this may be, you can bulletproof the approach a little more by combining your technique with this one: surround the iframe with a hidden div that you remove when you're finished parsing.
Here I go with another attempt.
Although I believe you already have the possibly best solution for this, I could only imagine that you work around it with a fairly low-performance method of calling esi:insert in a separate HTML window, then retrieve the contents as if you were using AJAX on the server. Perhaps similar to this? Then check the contents you retrieved, maybe by using json_decode and on success generate an error JSON string.
The greatest downside I see to this is that I believe this would be very consuming and most likely even delays your requests as the separate page is called as if your server yourself was a client, parsed, then sent back.
I'd honestly stick to your current solution.
this is a rather tricky problem with no real elegant solution, if not with no solution at all
I asked you if it was an HTML(5) or XHTML(5) document, because in the later case a CDATA section can be used to wrap the content, changing slightly your solution to something like this :
...
<b id='esi-data' style='display:none;'>
<![CDATA[ <esi:include src='apiurl/data'> ]]>
</b>
<script>
try{
var data = $.parseJSON($('#esi-data').html());
}catch{ alert('manage the error here');}
....
Of crouse this solution works if :
you're using XHTML5 and
the error contains no CDATA section (because CDATA section nesting is impossible).
I don't know if switching from one serialization to the other is an option, but I wanted to clarify the intent of my question. It will hopefully help you out :).
Can't you simply change your API to return JSON { "error":"error_code_or_text" } on error? You can even do something meaningful in your interface to alert user about error if you do it that way.
<script>var data = 999;</script>
<script>
data = <esi:include src='apiurl/data'>;
</script>
<script>
if(data == 999) alert("there was an error");
</script>
If there is an error and "data" is not JSON, then a javascript error will be thrown. The next script block will pick that up.
I am trying to show some JS code in a textarea. The code is generated with JS so I am injecting it into the textarea with JS. However, using the <script> tags, causes the script to execute. I thought using < would solve this, but this is simply displaying < instead of <.
Any suggestions how I can do this?
$('myTextarea').set('value', '<script>alert('do something');</script>');
Just separate the script tag into two.
$('myTextarea').val('<script>alert("do something");</scr'+'ipt>');
The next </script> after the opening <script> block closes the script block; whether it's contained with a JS string or not.
To fix you can either split the </script> like so;
$('myTextarea').set('value', '<script>alert('do something');</scr' + 'ipt>');
Or like this (less common, but works, and probably more correct);
$('myTextarea').set('value', '<script>alert('do something');<\/script>');
Furthermore, you also need to fix your quotes;
$('myTextarea').set('value', '<script>alert(\'do something\');<\/script>');
You can see this now working here: http://jsfiddle.net/pK9SK/
I'm extremely new to coding and I'm reading a book on it. And I think I have the basics down on this little test project I'm doing, but whenever I test the page I just see the code I used. Here's the entirety of my code.
<script type = "text/javascript">;
//<![CDATA[
// from concat.html
var person = "" ;
person = prompt( "What is your name?") ;
alert("Hi there, ") + person + "!");
//]]>
</script>
Honestly I don't know what the CDATA is for or what concat.html is.
How can I get Firefox to run my JavaScript rather than just show the code?
Try wrapping it in <html> to make the whole page get treated as HTML. Does the file have a .js extention, by any chance?
CDATA is to distinguish code from markup.
Put it in an HTML file.
So, first, save it as scriptname.html - you're embedding JavaScript within an HTML file.
Next, make it valid html - add <html> to the top and </html> to the bottom. And <head> and <body> tags where appropriate - if you don't know what those are, head over to any HTML site to look them up (www.diveintohtml5.org is nice, if you can follow it.)
Better install Firebug plugin for Firefox or use other browser's Javascript console. It will allow you to run your code
http://www.w3resource.com/web-development-tools/execute-JavaScript-on-the-fly-with-Firebug.php