I created a little demo here: https://codepen.io/min-width/pen/LYVVLwK
But just in case that gets lost in time, here is the JavaScript from that page (it's constructing a script tag with a type of "module" and injecting it into the page):
let value = `<script type="module">
let element = document.createElement("p");
element.appendChild(document.createTextNode("The JavaScript executed."));
document.querySelector("body").appendChild(element);
</scr` + `ipt>`;
let compiled =
document.createRange().createContextualFragment(value),
body = document.querySelector('body');
body.appendChild(compiled);
When working, it should say "The JavaScript executed" in the body of the page. This works fine in most browsers (and I accept that it doesn't work in IE since IE doesn't support modules at all).
My issue is that this does not work in Edge, even though Edge does have support for JavaScript modules: https://caniuse.com/#feat=es6-module
Edge just doesn't seem to like when modules are injected dynamically. Is there some client-side workaround for this? I currently have it working with a server-side check for "Edge" in the user agent (in which case I render the module immediately in the HTML rather than injecting it with JavaScript), but that doesn't feel like an optimal solution.
In case you are wondering, my use case is that I preload some JavaScript modules, then I inject them into the page when the user first interacts with the page (a page speed optimization that PageSpeed Insights seems to like).
Edit: Since somebody asked, here is the Edge version number info (from the settings):
It shows Edge 44 and EdgeHTML 18.
I tried to modify your code and it works with MS Edge 44 version.
<!DOCTYPE html>
<html>
<head>
<title>Demo page</title>
</head>
<body>
<script>
let se =document.createElement('script');
se.setAttribute('type', 'module');
se.innerHTML='var para = document.createElement("P"); para.appendChild (document.createTextNode("The JavaScript executed.")); document.querySelector("body").appendChild(para);';
let body = document.querySelector('body');
body.append(se);
</script>
</body>
</html>
Output in Edge browser:
Related
I have built a mobile webapp in html/js and I am using the 'window.onerror' to track client side js errors and report them to the server.
Recently I noticed that I receive many reports of ScriptError on line 0.
A quick google search brought me to discussions like Cryptic "Script Error." reported in Javascript in Chrome and Firefox and https://ravikiranj.net/posts/2014/code/how-fix-cryptic-script-error-javascript/
For most people such errors seem to be triggered by failures in scripts that are included from a foreign origin. But unfortunately that's not the case for me.
After many hours of debugging I came up with the following minimal example to reproduce the error:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>
<meta name="google" content="notranslate"/>
<script type="text/javascript">
function init() {
window.onerror = function() {
alert(JSON.stringify(arguments));
}
}
</script>
</head>
<body onload="init()">
<h1>Hello</h1>
<svg width="300" height="300">
<a href="#">
<rect x="50" y="50" width="200" height="200" />
</a>
</svg>
<p>
Link
</p>
</body>
</html>
As we can see it's a simple html page with no javascript at all except for the onerror handler itself.
In Safari (ios 12.4.1) no error is triggered. But in both Google Chrome for iOS and Firefox for iOS a ScriptError on line 0 is reported each time the link inside the <svg> element is tapped - but not when tapping the link outside the <svg>. Note that no js event handler is attached to the <svg>.
A live version of the code is online at https://static.laszlokorte.de/scripterror.php
I know that all third party browsers on iOS actually just use safari under the hood extending in with a few custom features. I suspect that those custom features cause the error but I have no idea how to track it down further.
Why does clicking a link inside a plain svg element causes an error in iOS Chrome and Firefox but not in Safari? Is there a way to connect the Safari Webdev tools to mobile browsers other than native Safari?
Update
After taking a deeper look at both the Chrome(iOS) and Firefox(iOS) source code I now found the cause of the problem. Both browsers inject javascript code that runs on touchstart and processes the event's target node. Both browsers check if an '' or '' element has been touched and try to access their their href or src attribute respectively. They both then try to send the attributes values via webkit.postMessage to the host application.
The error seems to be caused by the fact that an <a> element inside an <svg> is actually an SVGAElement and it's href attribute is not a String but an SVGAnimatedString which (other than String) can not be cloned to be sent to another process.
The corresponding code for Firefox can be found here:
https://github.com/mozilla-mobile/firefox-ios/blob/826cb396a9eb225b7eb667b47b245e2d98d26ed8/Client/Frontend/UserContent/UserScripts/AllFrames/AtDocumentEnd/ContextMenu.js#L14-L35
The code for Chrome is here:
https://github.com/chromium/chromium/blob/master/ios/web/web_state/js/resources/all_frames_context_menu.js#L136-L139
So for Chrome the solution is actually simple because it allows to explicitly opt-out of all this by setting the css property -webkit-touch-callout: none (You can see the check for that in the linked file). For example like this:
#namespace svg url(http://www.w3.org/2000/svg);
svg|a {
-webkit-touch-callout: none;
}
For Firefox I have not found a easy solution yet.
Update 2
For Firefox I now came up with the following monkey patch solution:
(function() {
var origClosest = SVGElement.prototype.closest;
SVGElement.prototype.closest = function (sel) {
var c = origClosest.apply(this, arguments);
if(c && sel === 'a' && c.namespaceURI === this.namespaceURI) {
return c.parentNode && c.parentNode.closest(sel);
} else {
return c;
}
};
})();
I override the closest method (that Firefox itself uses to find the touched <a>) on an SVG elements with my own method that skips all <a> elements that belong to the SVG namespace.
Additionally I submitted an issue in the Firefox repo at Github and an issue for Chromium.
I'm having some troubles with an iOS Smart App Banner, which I'm trying to add through JavaScript.
The actual smartbanner is as simple as adding this little block to the head of the HTML:
<meta name="apple-itunes-app" content="app-id=375380948">
Unfortunately, I'm quite restricted in the way I can upload my script. I can't change the HTML directly, so I'll do it through our Tag Manager, which basically does it through JavaScript. But it turns out that this doesn't work.
I've tried to simplify the case for testing:
Hardcoded tag in the HTML: works (as expected)
<meta name="apple-itunes-app" content="app-id=375380948">
Inserted with JavaScript directly when the document is ready: works
$(document).ready(function(){
$("head").append('<meta name="apple-itunes-app" content="app-id=375380948">');
});
Inserted with JavaScript, after a setTimeout delay: DOES NOT WORK
$(document).ready(function(){
setTimeout(showBanner, 1); //after 1 millisecond
});
function showBanner(){
$("head").append('<meta name="apple-itunes-app" content="app-id=375380948">');
}
Can anyone confirm or explain why this delayed JavaScript doesn't work?
Important: for testing open the page on an actual iOS device!
Desktop Safari/Chrome or iOS emulator won't work.
Also, don't close the banner, because it won't show up a second time.
UPDATE:
I've added some examples without jQuery, so plain 'ol JavaScript. But the results are the same. As soon as we wait for a setTimeout() the Smart App Banner fails to load.
Vanilla JavaScript - direct execution. works
showBanner();
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
Vanilla JavaScript - delayed execution. DOES NOT WORK
setTimeout(showBanner, 1);
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
UPDATE 2:
The exact same unfortunate behavior can be observed when loading the script asynchronously
async loading. DOES NOT WORK
<script src="async.js" async></script>
Which then calls the same vanilla JavaScript with direct execution
showBanner();
function showBanner() {
var meta = document.createElement("meta");
meta.name = "apple-itunes-app";
meta.content = "app-id=375380948";
document.head.appendChild(meta);
}
CONCLUSION:
I can only conclude that iOS Safari only looks for the smartbanner the HTML in the primary thread. And that makes me sad :-(
Only the directly available HTML, or HTML that is added synchronously through JavaScript.
But no a-sync action is allowed, be it loading the JavaScript asynchronously, or using setTimeout() (or an other construct that uses eval())
Since it is not possible to have safari show the native banner if it is inserted after the page load (As tested above, and confirmed by apple), the only viable workaround is not to use the native smartbanner, but to create your own.
For instance with a javascript plugin like this
As of April 2017, the smart banner will show up if added later via JavaScript.
CONTEXT
I am creating an http://canvimation.github.com/ that uses javascript to create web pages dynamically.
The source code for my application is at https://github.com/canvimation/canvimation.github.com
The application uses Canvas to draw using vector objects the resulting drawing can then be exported as a web page. To create a web page click on the FILE icon and then 'Export Canvas to HTML'.
The exporting code opens a new window and uses document.writenl() to construct the code. The relevant function is shown at the end of the question.
All browsers I have tried produce the drawing correctly in the new window as a web page. In all browsers view source shows the expected code for the application web page.
In IE10 and Firefox it is possible to view the source of the exported code which can then be saved and the saved file used as a webpage.
For the exported web page: in Chrome 28 view source is greyed out and not available; in Safari 5 for Windows view source produces a blank window and in Opera 12 source does not do anything.
QUESTIONS
How is it possible to save or view the source of the exported webpage in Chrome, Safari and Opera?
Can anything be changed in my method to dynamically create a webpage that can have its source code viewed and saved?
RELEVANT CODE
function exportShapes()
{
var shapelist=[];
var nameZ=[];
for (var name in SHAPES)
{
nameZ=[];
nameZ.push(name);
nameZ.push(SHAPES[name].zIndex);
shapelist.push(nameZ);
}
shapelist.sort(compareZ);
newwindow=window.open('','export');
newwindow.document.writeln('<!DOCTYPE HTML>');
newwindow.document.writeln('<html>');
newwindow.document.writeln(SPACES.substr(0,3)+'<head>');
newwindow.document.writeln(SPACES.substr(0,6)+'<style>');
newwindow.document.writeln(SPACES.substr(0,9)+'#canvasarea');
newwindow.document.writeln(SPACES.substr(0,9)+'{');
newwindow.document.writeln(SPACES.substr(0,12)+'border:black 1px solid;');
newwindow.document.writeln (SPACES.substr(0,9)+'}');
newwindow.document.writeln(SPACES.substr(0,6)+'</style>');
newwindow.document.writeln(SPACES.substr(0,6)+'<!--[IF LT IE 9]><script type="text/javascript" src = "excanvas.js" ></script><![endif]-->');
newwindow.document.writeln(SPACES.substr(0,6)+'<script type="text/javascript">');
newwindow.document.writeln(SPACES.substr(0,9)+'function setcanvas()');
newwindow.document.writeln(SPACES.substr(0,9)+'{');
newwindow.document.writeln(SPACES.substr(0,12)+'var canvas = document.getElementById("canvasarea");');
newwindow.document.writeln(SPACES.substr(0,12)+'if (canvas.getContext)');
newwindow.document.writeln(SPACES.substr(0,12)+'{');
newwindow.document.writeln(SPACES.substr(0,15)+'var ctx = canvas.getContext("2d");');
newwindow.document.writeln(SPACES.substr(0,15)+'drawcanvas(ctx);');
newwindow.document.writeln (SPACES.substr(0,12)+'}');
newwindow.document.writeln(SPACES.substr(0,12)+'else');
newwindow.document.writeln(SPACES.substr(0,12)+'{');
newwindow.document.writeln(SPACES.substr(0,15)+'alert("Canvas NOT supported");');
newwindow.document.writeln (SPACES.substr(0,12)+'}');
newwindow.document.writeln (SPACES.substr(0,9)+'}');
newwindow.document.writeln (SPACES.substr(0,9));
newwindow.document.writeln (SPACES.substr(0,9)+'function drawcanvas(ctx)');
newwindow.document.writeln(SPACES.substr(0,9)+'{');
for(var i=0;i<shapelist.length;i++)
{
exportshape(shapelist[i][0]);
}
newwindow.document.writeln (SPACES.substr(0,9)+'}');
newwindow.document.writeln(SPACES.substr(0,6)+'</script>');
newwindow.document.writeln(SPACES.substr(0,3)+'</head>');
newwindow.document.writeln(SPACES.substr(0,3)+'<body onload="setcanvas()">');
newwindow.document.writeln(SPACES.substr(0,6)+'<canvas id="canvasarea" width="'+parseInt($("stagearea").style.width)+'" height="'+parseInt($("stagearea").style.height)+'"></canvas>');
newwindow.document.writeln(SPACES.substr(0,3)+'</body>');
newwindow.document.writeln('</html>');
newwindow.document.close();
}
If you are just trying to see the HTML after javascript rendering, then right-click > inspect element should let you see the code, at least in Chrome.
You should then be able to copy it manually - or do you need it to be done automatically?
I know in brython it's just in the variable doc.html, but that probably doesn't help you in javascript.
Similar Question
The problem is it's a blank page (about:blank), for which Chrome sees no reason to view the source code, because it should be empty. You may consider creating some sort of temp file and put all the canvas stuff into it.
Inspect Element option, suggested above, doesn't work in this case, it's not going beyond canvas tag. I suspect Chrome is rendering this as a picture, not as a vector graphics.
In firefox 19 and firebug 1.X I encountered a strange bug when trying //#sourceurl.
Basically I'm dynamically adding script tag through dom manipulation as you can see in the sample below. This does not work.
Maybe it's a limitation of ff but I find it odd that it works in chrome and not in ff.
Can you confirm this and do you have any bypass to this bug?
Ps: I don't want to use global eval() as it crash in ie when using document.write
<html>
<head>
<script type="text/javascript">
var count=0;
function addNewScriptToHead()
{
var newScriptElem;
var newScriptText;
newScriptElem = document.createElement('script');
newScriptElem.setAttribute('type', 'text/javascript');
newScriptElem.setAttribute('id', '' + count);
newScriptElem.text= 'console.log("Yay !");//# sourceURL=root/test'+count++ +'.js';
document.body.appendChild(newScriptElem);
};
</script>
</head>
<body>
<button onclick="addNewScriptToHead()">add script</button><br><br>
</body>
</html>
Experimentation has lead me to believe the following:
As of version 20.0, FF still does not support //# sourceURL directly in its inbuilt web console.
//# sourceURL does work with the Firebug addon in FF, but not completely as expected/hoped.
A. It only works for eval. It doesn't work at all for appended
script nodes like in the original question.
B. Errors will have a line number and the URL, and you can click
the error to see the line of code. However, console.log does not
seem to be affected and shows no line number or URL.
Testing this feature from within FF's web console is not advised. I got different results than testing directly in HTML at least some of the time.
Consider the following HTML:
<!DOCTYPE html>
<html>
<body>
Test page
<!--Start of Zopim Live Chat Script-->
<script type="text/javascript">
window.$zopim||(function(d,s){var z=$zopim=function(c){z._.push(c)},$=z.s=
d.createElement(s),e=d.getElementsByTagName(s)[0];z.set=function(o){z.set.
_.push(o)};z._=[];z.set._=[];$.async=!0;$.setAttribute('charset','utf-8');
$.src='//cdn.zopim.com/?pTR0FiicfJ4aMcmuHI9HfVAB4uzKeFIT';z.t=+new Date;$.
type='text/javascript';e.parentNode.insertBefore($,e)})(document,'script');
</script>
<!--End of Zopim Live Chat Script-->
<script type="text/javascript" async="">
window.addEventListener('load', function() {
var b = document.getElementsByTagName("body")[0];
var o = document.createElement("object");
o.setAttribute("type", "application/x-shockwave-flash");
var t = b.appendChild(o);
});
</script>
</body>
</html>
jsFiddle here: http://jsfiddle.net/V9jtD/
It contains Zopim widget code, another async script that just adds an "object" tag to DOM. Let the above file be served by a webserver (you can use the jsFiddle: http://fiddle.jshell.net/V9jtD/show/). Open it in MacOSX Firefox 15+ with Firebug activated.
You will see the following error repeated many times:
TypeError: non-native scope object
If the errors donot show up, just refresh the page. And mainly Zopim fails to show up (sometimes appears with incomplete functionality). I could not reproduce this in Firefox/Ubuntu. (It might be reproducible in Firefox/Windows I could not check).
However, in the following cases Zopim loads fine:
Comment out "var t = b.appendChild(o);" (ie. dont append the "object" tag to DOM, or you can just remove second script tag entirely).
Deactivate Firebug.
I essentially want to understand who is causing the error so I can debug it.
Is it Firebug, because deactivating it Zopim works fine? But then removing the second script tag with Firebug enabled does not cause the error.
Is it the second script tag, because removing it Zopim works fine? But then deactivate Firebug and Zopim works fine even with second script tag.
Or is it Zopim itself?
Note that the content in the second script tag is part of function testPlayerVersion() in swfobject.js. I have hit this error when I am trying to add Zopim to a page that already has swfobject.
It would probably be easier to work out if the fiddle wasn't loading minified JS from Zopim, but the offending line (roughly beautified) is:
try {
(0)()
} catch (v) {
s = v.arguments ?
'chrome' : v.stack ?
'firefox' : window.opera && !('stacktrace' in v) ?
'opera' : 'other'
}
Which looks like a hacky way of detecting the browser. I'm not sure why it's causing Firebug to bail, it might be worth raising an issue with them if it's reproducible.