HTML Templates and HTML Import - Inner Script not Executing in Firefox - javascript

I am using webcomponents.js to polyfill Firefox support of web components.
When loading an HTML template through an HTML import, a script within the template does not execute once the custom-element based on the template is added to the DOM of the main page.
It does execute when running the site in Chrome (which has native web components support).
However, both Firefox and Chrome execute such script when the template is placed within the main page itself.
See the example from Eric Bidelmann at HTML5Rocks. This runs both in Chrome and in Firefox (via webcomponents.js) - as long as the template is located in the same HTML file.
<!DOCTYPE html>
<html>
<body>
<button onclick="useIt()">Use me</button>
<div id="container"></div>
<script>
function useIt() {
var content = document.querySelector('template').content;
// Update something in the template DOM.
var span = content.querySelector('span');
span.textContent = parseInt(span.textContent) + 1;
document.querySelector('#container').appendChild(
document.importNode(content, true));
}
</script>
<template>
<div>Template used: <span>0</span></div>
<script>alert('Thanks!')</script>
</template>
</body>
</html>
But while the template text shows, it does NOT run when loading the template and its content via a HTML import link. As mentioned, it loads the content, but isn't executing the script in Firefox.
Is this a bug in webcomponents.js, or a bug in Firefox?
Anyone having a good idea for workarounds (other than "use Polymer or Angular")?
Note - this is based on using Firefox 43 and Chrome 47.

It is not a bug in Firefox, Internet Explorer 11 has the same behaviour (with webcomponents.js).
The importNode method is not polyfilled by webcomponents.js. That's why <script> elements are not activated when they are imported.
There an issue opened related to that problem.
My workaround: I don't insert scripts tags <script> inside an imported <template> (JS/HTML separated:).
Also, you could parse your <template> looking for <script> tags and try to activate/execute them manually (could be somewhat tricky...)

I did find a workaround for this issue which allows for script execution when the template element is activated.
This relies on the fact that HTML imports execute scripts located in the imported file immediately if they are not located inside a template tag.
So the solution is to just add one more HTML import inside the import file and load the script as external resource - this gets loaded once the template activates and executes right then and there.
So instead of having the below in the external import file:
<template>
<div>Template used: <span>0</span></div>
<script>alert('Thanks!')</script>
</template>
you have THIS:
<template>
<div>Template used: <span>0</span></div>
<link rel="import" href="script1.html">
</template>
and the referenced script1.html file contains the following:
<script>
alert('Thanks!')
</script>
Yes, it's one more HTTP request, but it's workable and simple.
This solves any script which must run upon initialization of a template element. Script needed for later activation can be materialized in terms of function definitions, or inline scripts.
Note:
Another workaround is to add a "div" tag in the external import file below (and outside) the template section with an id="script1" and then use some JS to extract the div's content and add a script tag instead into the template. Since you anyway need JS to activate the template it's not much of a change,but this feels more like a hack.

Related

How to correctly add an external script to a ReactJS component in Gatsby.js project?

I am trying to add a Trustpilot widget to my Gatsby.js website. To make the Widget styling work, it is required to load an external script from Trustpilot CDN.
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
I have tried multiple ways to add this script to my footer component. The first thing I tried was React Helmet. I added using the following code:
<Helmet>
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
</Helmet>
The script seems to load and the styling works when I initially load a page. Once I navigate to a different page, the styling goes away. As I reload, it comes back.
I then tried adding the script inside componentDidMount()
componentDidMount() {
var addScript = document.createElement('script');
addScript.setAttribute('src', '//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js');
document.body.appendChild(addScript);
}
But even that yields the same result.
What's going on here? Is the external js script going away? How can I load it correctly so that it stays?
When you navigate away the parent component is being unmounted and Helmet removes the corresponding script tag from the head. To avoid this, place the component above the page component, e.g. using wrapPageElement or wrapRootElement as needed.
Naive scripts that inject code into the DOM are going to be problematic. React has some notes and tips on this in the documentation. Typically I will search to see if the vendor has any documentation on integrating with a single-page app. If not, you might need to do some code spelunking to figure out how to best support the desired functionality within the constraints.

How to load HTML file containing <template> element?

I want to re-use my custom web component. My web pages are primarily defined by HTML files, not Javascript. Is there any viable way to insert my component into my main file using HTML/Javascript/jQuery? (I know I could assemble it via server-side programming, but that's far less reusable.)
Simple Example
File widget.html:
<template>
// some content
</template
Main file main.html:
<div id="foo"></div>
<script>
$("#foo").load("widget.html); // does not work.
</script>
Note that this code fails for only the <template> ... </template> portion of any file I try to load. If I place other valid (and even invalid) HTML elements such as <div> or <foobar>, those elements get inserted into my page.

Foundation 6 - Console Warning : Tried to initialize magellan (any JS plugin) on an element that already has a Foundation plugin

I installed Foundation 6 using bower. I keep getting multiple warning in the console every-time I use any Foundation 6 - JavaScript based plugin.
Exact warning :
Tried to initialize magellan on an element that already has a Foundation plugin.
My script includes look like:
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/what-input/what-input.js"></script>
<script src="bower_components/foundation-sites/dist/foundation.js"></script>
<script src="js/app.js"></script>
<script type="text/javascript">
$(document).foundation();
</script>
The warning is triggered by the following code present in foundation.js at line 180 :
// For each plugin found, initialize it
$elem.each(function () {
var $el = $(this),
opts = {};
// Don't double-dip on plugins
if ($el.data('zfPlugin')) {
console.warn("Tried to initialize " + name + " on an element that already has a Foundation plugin.");
return;
}
I have tried re-installing from scratch but it still doesn't work.
Similar question exists in Zurb Foundation Forum but till now there is no good answer.
How do I resolve this?
UPDATE
For Version 6.4.*, all plugins and js based elements tend to work only if $(document).foundation(); is mentioned within the html file. So delete it from app.js if you have written in it. For additional reasons, go through the explanation below.
Although I have mentioned the answer long back in the comments of the question, I felt it will be better to write it up with few more points.
While installing Zurb Foundation 6 using bower (command line), it gives you a nice index.html page to get started which has the <script> tag that refers to an external js file located at root\js\app.js. The app.js file by default has the following code:
$(document).foundation(); //invoke all jQuery based Foundation elements
So basically, you already have everything to get started.
But, this wasn't the way how it worked until Foundation 6 and I wasn't aware of these changes. I didn't go through the contents of app.js as I was assuming it to be empty. I simply did the old way of invoking them in my html pages by writing the following code:
<script type="text/javascript">
$(document).foundation();
</script>
This kind of double referred the jQuery based Foundation elements raising a warning at the browser console.
Solution was to remove either of the invocation, but actually removing the code written in external js file makes more sense for the following reasons:
External references cost an additional http request, very slightly increasing the precious page load time. Why not reduce it, if you can.
Invoking jQuery based elements in the site has to be done as early as possible so that every element gets the original shape in no time. So it makes more sense to mention the invocation within a html page rather than request a external file to invoke.
So, remove the code mentioned in the external js file, that is your app.js file. The warnings would vanish.
For Version 6.4.*, all plugins and js based elements tend to work only if mentioned within the html file.
The best solution to this problem is to put $(document).foundation(); inside <script> tag under .html file, and remove it form external .js file (app.js)
Like this-,
in
xyz.html
<script type="text/javascript">
$(document).foundation();
</script>
other solution like, putting it inside external .js file (app.js), and removing it from .html file, will not always works.
at least it didn't work for me.
Tested on-
foundation version 6.4.2

How to have a script tag apply to a certain element, but not others

I am working on a project that requires two angularjs files, textAngular-sanitize and angular-sanitize. When both of these files are loaded in the header or footer, the part of the document that requires the file that is loaded first will work, however the element that requires the file that is loaded after the first ceases to work. I.e when angular-sanitize is loaded first, then the textAngular dependent element stops working, whereas if the textAangular file is loaded first, then the angular-sanitize element stops working. This is presumably because both files are loaded and run over the entire document. Is there a way to confine the 'scope' of one of the files to only one element on the page so that both scripts can be run without interfering with one another?
Or perhaps, is there an alternative method of loading external scripts other than the <script> tag?
Here is an example of what is going on:
<head ng-app="myApp">
<script src="scripts/textAngular.min.js"></script>
<script src="scripts/angular-sanitize.min.js"></script>
</head>
<body ng-controller="MainController" ng-init="initialize()">
<div id="ngSanitizeElement">
<!-- This element won't work -->
</div>
<div id="textAngularSanitizeElement">
<!-- This element will work -->
</div>
</body>
Not sure to understand your problem, but if you're looking for something to load external files from your controllers or your router, try ocLazyLoad : https://oclazyload.readme.io/docs
Which backend do you use? PHP? I would then put in the script tag the value of the right file depending?
Or can't you use 2 different ng-controllers?
<div ng-controller="textAngular ">
<div ng-controlelr="angular-sanitize">

Why does this work as internal script but not when you use an external javascript file

I am just learning Javascript and need an answer to this question.
Why doesn't this piece of javascript work in the HTML DOM using an internal script tag?
Here is my html doc. with an internal javascript extension:
<div id="targetarea">
<p>Hello World</p>
</div>
<div id="target-area">
<p id="tagline">Hello World!</p>
</div>
<script>
//Creating a a new element
// store the target area to a variable to keep things neat
var targetArea = document.getElementById("target-area");
// create our <p> element
var p = document.createElement("p");
// create a text node inside the <p>, note that we're
// using a variable "p" here
var snippet = document.createTextNode("this was a generated paragraph");
// insert our generated paragraph into the DOM
p.appendChild(snippet);
targetArea.appendChild(p);
</script>
This works fine internally but when use an external js file it does not. Can someone give me the right js snippet for this to work in an external file and explain why?
It depends when you're running the script.
If you load the script in the <head> section of the file the DOM of the page has not been loaded yet and therefore for example getElementById is going to fail.
Loading your external scripts as the last thing in the <body> part will solve this problem.
If the code runs fine internally but not externally,the problem could be linking the JS file. Check your link to the JS file and make sure it is linked correctly. If you linked the javascript file like this
<script src="javascriptFile.js">
</script>
and the code still fails to run correctly, then the problem may lie elsewhere. Also make sure you are loading scripts inside of the body, and make sure the file path is correct.
You would be suprised how often small mistakes like typos cause problems like these.

Categories