Invoking document.write() - javascript

This code does not work:
<div class="pix"><div id="addTimestamp"></div></div>
<script type="text/javascript">
(function () {
var date = new Date(),
timestamp = date.getTime(),
newScript = document.createElement("script");
newScript.type = 'text/javascript';
newScript.src = 'someUrl=' + timestamp + '?';
document.getElementById('addTimestamp').appendChild(newScript);
}())
</script>
The dynamic script adds document.write(someCode which loads banners). But in Firebug I have an error:
Invoking document.write() from asynchronously-loaded external script was ignored.

Add this:
newScript.async = false;
Your script needs to load synchronously for document.write() to work (see https://developer.mozilla.org/En/HTML/Element/Script#attr-async). As you have it now, the script will load whenever the browser has time for it - so you cannot know where your HTML from document.write() will be inserted. The browser decided to ignore your document.write() call to prevent worse issues.

document writing javascript causes the html parser to fail when seeing try
document.getElementById('addTimestamp').innerHTML = '<script type="text/javascript" src="someUrl=' + timestamp + '?"' + 'charset="utf-8"></sc' + 'ript>';
However if you want to insert a script tag in in the DOM you need to also be certain the DOM is loaded.

Levon, it seems like you are trying to overcome problems with page load speed (I don't see other reasons not to just insert script statically).
Wladimir's answer is good and valid, but see my comment to his answer.
Another approach, which works, but should be very carefully implemented, is to overwrite the document.write itself. It is very, very, subtle work, it need to be thoroughly tested, but it can be done, actually. Each call of document.write can store something to some sort of string buffer. Then, by deciding somehow that it is time to flush all the content, just insert all buffers content to some DOM element.
Works, but very pervy. The best option is not touse document.write at all. But, alas, it is not always the option.

Related

Locate and check script (within webpage) is correct using nightwatchjs

I'm using nightwatchjs as my testing tool and I need to test that an injected script is correctly displayed on a page, and that the script is correctly populated.
So, the following script html is to be tested (to ensure that it is correctly displayed):
<script type="text/javascript">
(function() {
window.dm = window.dm ||{ AjaxData:[]};
window.dm.AjaxEvent = function(et, d, ssid, ad) {
dm.AjaxData.push({ et:et, d:d, ssid:ssid, ad:ad});
window.DotMetricsObj && DotMetricsObj.onAjaxDataUpdate();
};
var d = document, h = d.getElementsByTagName('head')[0], s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src='https://uk-script.dotmetrics.net/door.js?id=11373';
h.appendChild(s);
} ());
</script>
So firstly, I'd like to test that this script is present on the page, but in a way that is as less brittle as possible. I can test for /html/head/script[13]/text() but this is really brittle and far from ideal.
Is there something in the script itself that I can reference, so the test won't be as brittle?
Secondly, I want to ensure that the script details are correct. This can be a test that will check for the presence of the https://uk-script.dotmetrics.net part of the script for example.
However, I've tried to use my usual css and xpath ways of locating this part of the script but with no luck.
Any help would be appreciated. Thanks.
In line with the paradigm of "assert the what, not the how", the "what" here seems to be making sure that a script got injected into the page. You could use a selector like this one: script[src^="https://uk-script.dotmetrics.net/"], in conjunction with Nightwatch's element function. You could add more selectors to assert the script has the expected attributes like type and async.
In this way, you can skip the implementation detail (the presence of the injecting script) and focus on what I think you really care about (that a script with a correct URL got injected into the page).

How to measures script execution and *parsing* time?

As far as I know, scripts are downloaded and executed synchronously in javascript.
Hence if we write the following code:
<script type='text/javascript'>console.time('core')</script>
<script type='text/javascript' src="guicore.js"></script>
<script type='text/javascript'>console.timeEnd('core')</script>
we'll see in console total time for download, parse and execute js.
How we can exclude parsing time? Just add similar file, but with all code commented out. More or less, this technique should work.
The problem is this just doesn't work =)
I optimized that code, reduce execution time from 90ms to 25ms, but see the same ~100±10ms time for Chrome and ~160±15ms for Firefox.
Ok, I know I could use profiler, but the question is: "how to measure js parsing time correctly" and what did I measured btw. Research.reverse-engineering is very fun, but maybe there's someone who knows that field in depth.
You cannot accurately measure script parse time independent of execution time using web APIs alone. Different browsers have different strategies for when they do parsing, and some of them will parse progressively as the script is executed, or even do "partial parsing" in cases where it's assumed a block of code is unlikely to immediately be executed (e.g. a function that is not an IIFE, see some details in the optimize-js README).
Using separate <script> tags is the most accurate way to at least capture both parsing and execution time. The only modification I would make to your snippet is this:
<script>
performance.mark('start');
</script>
<script src="myscript.js"></script>
<script>
performance.mark('end');
performance.measure('total', 'start', 'end');
</script>
Note the use of the high-precision User Timing API which, as an added bonus, will show visualizations in the Chrome/Edge/IE dev tools (and tools like Windows Performance Analyzer and WebPageTest if you're so inclined).
Technically the 3rd <script> is not necessary, as you can just append the mark/measure to the end of the 2nd script. But the 1st <script> is certainly necessary to capture all parse time. You can verify in the dev tools that the marks/measures encompass all initial parsing and execution time.
I know this is kind of an old question but I came across it while looking for a solution to this myself. You can use the dev tools in the browser of your choice to look at this but if you'd like to do it in code this is the method I ended up using.
The scriptLoadParseDuration function below will take a URL to a .js file, place it into a <script> element, and log the load/parse duration to the console.
Keep in mind that this will execute the <script> you are profiling within the current DOM context. So in the example below: jQuery is still accessible in the global scope even though the script was removed. The script could be extended to do all of this in an <iframe> to isolate it though.
function scriptLoadParseDuration(url) {
var start;
var script = document.createElement('script');
// <script> must be attached to the document to actually load the file
document.querySelector('html').appendChild(script);
// Calculate load/parse duration once script has loaded
script.addEventListener('load', function scriptLoad() {
// Calculate load/parse duration
console.log('Duration: ' + (Date.now() - start) + 'ms');
// Remove <script> from document
script.parentElement.removeChild(script);
}, false);
// Get current time in milliseconds
start = Date.now();
// Setting the `src` starts the loading. Math.random is used to make sure it is an uncached request
script.src = url + '?' + Math.floor(Math.random() * 9e9);
}
var url = 'https://code.jquery.com/jquery-3.0.0.min.js';
scriptLoadParseDuration(url);
Here is an example showing that jQuery is still in the global scope after the <script> removal.
function scriptLoadParseDuration(url) {
var start;
var script = document.createElement('script');
console.log('`jQuery` before attaching: ' + typeof jQuery);
// <script> must be attached to the document to actually load the file
document.querySelector('html').appendChild(script);
// Calculate load/parse duration once script has loaded
script.addEventListener('load', function scriptLoad() {
// Calculate load/parse duration
console.log('Duration: ' + (Date.now() - start) + 'ms');
console.log('`jQuery` once attached: ' + typeof jQuery);
// Remove <script> from document
script.parentElement.removeChild(script);
console.log('`jQuery` after detach: ' + typeof jQuery);
}, false);
// Get current time in milliseconds
start = Date.now();
// Setting the `src` starts the loading. Math.random is used to make sure it is an uncached request
script.src = url + '?' + Math.floor(Math.random() * 9e9);
}
var url = 'https://code.jquery.com/jquery-3.0.0.min.js';
scriptLoadParseDuration(url);
Open up Chrome and open the developer tools, the go to the "Timeline" tab. If you press the record button (filled in circle, bottom left) then reload the page it'll give you a fairly detailed timeline, broken down into specific types of activity (Send Request, Parse, Evaluate), timed down to the microsecond.
Very old question with a relatively new answer.
Date.now() returns a timestamp with millisecond accuracy. For an application to run at 60FPS, it must update the frame every 16ms. Our millisecond meter may not be accurate enough.
Introducing the Performace API in modern JS browsers, this allows for floating-point timestamps accurate to the microsecond.
Instead of Date.now() use window.performance.now() for measurements, there's a good guide on using the Performance API on HTML5Rocks.
Chrome DevTools actually has a hidden flag that shows V8 Parse and Compile time!
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/javascript-startup-optimization#parsecompile
Outcome looks like this:
A quick guide is also available in the doc in the blue section below:
After you enable the feature, you can profile a page and then click on "Bottom-Up" tab on the Performance tab in Chrome DevTools, and then make sure you "Group by Activity", and you should see the Compile and Parse time now.
Enjoy!

How can I delay the execution of a script block until after an external script has loaded?

I'm trying to dynamically insert and execute a couple of scripts, and I think I'm hitting a race condition where the second is trying to execute before the first is loaded.
The project I'm working on has an unusual requirement: I am unable to modify the page's HTML source. It's compiled into an app for localization purposes.
Therefore, I'm unable to insert <script> tags like I normally would to link in JavaScript files.
It turns out that the client wants to use a hosted web font, so I decided to build and append the two required <script> tags dynamically in an already-linked JavaScript file.
The <script> blocks are appending correctly in the head of the document, but function in the second block seems to be firing before the external script linked in the first <script> tag is fully loaded, and it's throwing an undefined error.
Here's the relevant piece of code:
document.getElementsByTagName("head")[0];
var tag = document.createElement('script');
tag.setAttribute("src", "http://use.typekit.com/izj3fep.js");
document.getElementsByTagName("head")[0].appendChild(tag);
try {
Typekit.load(); // This is executing too quickly!
} catch(e){
console.log("Hosted fonts failed to load: " + e);
}
I tried moving the try block to the window.onload event, but that fires before any of this code is called.
I guess I could dynamically load jQuery and then use it's ready event, but that seems pretty heavy-handed. I'm hesitant to pull in a library on this project, as the client has a lot of custom JavaScript that could potentially clash with it.
What else can I try?
You need to hook into the script element's onload event and execute your code there:
document.getElementsByTagName("head")[0];
var tag = document.createElement('script');
tag.onload = onTagLoaded;
tag.setAttribute("src", "http://use.typekit.com/izj3fep.js");
document.getElementsByTagName("head")[0].appendChild(tag);
function onTagLoaded() {
try {
Typekit.load(); // This is executing too quickly!
} catch(e){
console.log("Hosted fonts failed to load: " + e);
}
}
You can load it with yepnope ( http://yepnopejs.com/ ). I know it's a library, but it's very light (free if your client is already using modernizr). It's well worth it. Hopefully the client doesn't have another yepnope function, and you don't have to worry about the clash.
Are you using jQuery? If not, I highly recommend it. It'll make your life so much easier:
$.getScript('http://use.typekit.com/izj3fep.js', function(data, textStatus){
try {
Typekit.load(); //executes properly now!
} catch(e) {
console.log("Hosted fonts failed to load: " + e);
}
});
Combining the scripts into one big seems to be the easiest solution.

Trying to load advertiser script tags asynchronously - not working

I've got some banner zones set up on advertisespace.com - I'm trying to load the script tags using jquery so they load after the page has loaded. However its not working. Here is my code to do this.
Here is the function I use to include the script tag:
function jsinclude(file, dom) {
if (document.createElement && document.getElementsByTagName) {
if(dom=='undefined')
var dom = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', file);
script.setAttribute('charset',"utf-8");
dom.appendChild(script);
} else {
alert('Your browser can\'t deal with the DOM standard. That means it\'s old. Go fix it!');
}
}
ANd here is how I am calling the function:
$(function(){
jsinclude('http://ads.advertisespace.com/somethingsomething.js', document.getElementById('location-of-banner-1'));
jsinclude('http://ads.advertisespace.com/somethingsomething.js', document.getElementById('location-of-banner-2'));
})
The result is that the script tag is inserted in the correct place but the banners doesn't show i.e the code in the script file referred to is never executed. How can I fix this.
You tagged your question with jquery so why not make use of its advantages.
The dom is not the head element, and doing dom == 'undefined' checks whether the variable dom is equal to the string 'undefined'. You must have set that explicitly and that's probably not the case. Anyway, there is one head element so it's not necessary to pass it to the function - the function can handle that itself.
Also, checking for DOM functions is not applicable these days. We live in 2011, all browsers have these functions included.
function jsinclude(file) {
var script = $("<script>", { type: 'text/javascript',
src: file,
charset: 'utf-8' });
$('head').append(script);
}

Print data instead of alert

I use the javascript to get some data from facebook using javascript sdk:
window.fbAsyncInit = function() {
FB.init({appId: '111164862286924', status: true, cookie: true,
xfbml: true});
/****************************************/
FB.api('/f8', function(response) {
alert(response.company_overview);
});
/****************************************/
};
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
This code works fine but if I change alert with documen.write to print the data instead of showing it inside the popup window it doesn't seem to work any more. It doesn't print anything. Can anyone please tell what could be the reason?
Thanks in advance
You can't use document.write after the initial parse of the page is complete. Fortunately, though, there's no particular reason to.
If you have an element on the page and you wish to append to it, you can do this:
var parent = document.getElementById('theId');
var p = document.createElement('p');
p.innerHTML = "This is the <em>new content</em>.";
parent.appendChild(p);
Live example (It doesn't have to be an p element, it can be a div or span or anything else that's a valid child of the parent element.)
Note that if you just want to append to the bottom of the document (relatively rare, but hey), you can just use document.body in place of parent:
var p = document.createElement('p');
p.innerHTML = "This is the new content.";
document.body.appendChild(p);
Similarly, if you want to replace the content of an element, just get a reference to it (using getElementById is a convenient way if the element has an ID, but there are other ways) and update it, rather than append to it:
var target = document.getElementById('targetId');
target.innerHTML = "This is the <em>updated content</em>.";
More re document.write: document.write actually writes to the HTML stream being parsed by the HTML parser. Naturally, once the page rendering is complete, there's no longer an HTML stream being parsed. But the DOM methods above are available for manipulating content on the page after the page is rendered.
Off-topic: DOM manipulation and DOM searching (finding elements to update) are both made easier (and in particular made easier cross-browser) if you use a library like jQuery, Prototype, YUI, Closure, or any of several others which will have syntactic sugar and workarounds for browser eccentricities, etc.
If you are looking to just output the value for testing. I recommend Console.JS for IE and Firebug for Firefox. They support a function called console.log() which will trace output to a debugging window.

Categories

Resources