Client-side Handlebars causes 404 requests - javascript

I want to use handlebars on the client side. In my html code I have calls like:
<img src="data/cloud/products/{{key}}/{{images.product.frontal.trans_bg_img}}" alt="">
directly in my index.html.
In javascript I do something like this:
this.emptyPageSource = $("#productdetail").html();
this.productTemplate = Handlebars.compile(this.emptyPageSource);
var html = this.productTemplate(product);
$("#productdetail").html(html);
which works fine. I take the existing piece of html from the dom as a template once, then apply the templating with handlebars and overwrite the old dom entry.
When I load the page, I get a lot of 404 requests, because the browser already tries to load the image resources, even if the templating wasn't invoked yet, due to the JS part.
Is there a way to evade the 404 get requests? (I'm not using angular or something alike - just plain js + jquery)
Thank you in advance
Chris

I will try converting #productdetail element to <script type='text/template' id='productdetail'>. As in: JSBin
<h1>A Cat</h1>
<script type="text/template" id="productdetail">
<img src="{{image}}" alt="">
</script>
Rest of it
<script>
var product = {
key: '1',
image: 'https://media.giphy.com/media/freTElrZl4zaU/giphy.gif'
}
var emptyPageSource = $("#productdetail").html();
var productTemplate = Handlebars.compile(emptyPageSource);
var html = productTemplate(product);
$("#productdetail").replaceWith(html);
</script>
The browser does not understand text/template scripts and just ignores its. But we can read the content inside our script tag use it as a template.

Related

How to make the browser run JavaScript code injected by DOM manipulation?

I want to serve a little HTML snippet that other pages can include.
The HTML content would look something like this:
<div id="coolIncludable" style="display:none"> <!--be invisible until you are finished initializing -->
<div>Cool stuff here</div>
<div>More cool stuff and so on...</div>
</div>
<script src="http://somehwere/jquery.js"></script>
<script src="http://somewhere/something.js"></script>
<script>
$(function(){$('#coolIncludable').show();});//<- actually contained in a script file, just for illustration
</script>
I'm planning to use the method detailed here: https://www.w3schools.com/howto/howto_html_include.asp to do the actual including. Let's say the page looks something like this:
<html>
<head>
</head>
<body>
<H1>Content before snippet</H1>
<div id="registerWrapper" html-include="http://somehwere/snippet.html">
No content loaded
</div>
<H1>Content after snippet</H1>
<script type="text/javascript" src="http://somehwere/html-include.js"></script>
</body>
</html>
The HTML snippet gets loaded and embedded all right, but the JavaScript that comes with it never gets executed. Is there a way to embed content including scripts that makes sure they are executed?
I don't expect to control the embedding page, so I cannot rely on it having jQuery or anything else loaded. I therefore avoided using jQuery in the embedding function and restricted myself to plain JavaScript. Loading jQuery is one of the things the <script> tags at the end of the snippets would do.
Since you are using jQuery you can use it's built in load() method to do what you want
Something like:
HTML
<div class="include" data-include="page2.html"></div>
JS
$('.include').each(function(){
const $el = $(this), url = $el.data('include');
$el.load(url)
});
Then make sure the script tag for the new content is below that content.
Simple working demo
In retrospect, my mistake is glaringly obvious:
What does the line $(function(){$('#coolIncludable').show();});
do? does it execute $('#coolIncludable').show();? No it doesn't. It registers a callback to do so that gets triggered by the 'load' event, which already has fired, and won't fire again.
On the other hand, that's really a moot point because the code never even gets executed.
Here's what I learned about dynamic loading of javascript
Injecting script tags directly does not work
script tags injected by setting element.innerHtml do not get executed
<div id="snippet">
...
</div>
<!-- neither of these will be executed -->
<script type="text/javascript">alert("stuff");</script>
<script type="text/javascript" src="http://somewhere/script.js"></script>
Creating script tags dynamically does work
What does work is dynamic tag generation the way it is described in this article: JavaScript Madness: Dynamic Script Loading
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'helper.js';
head.appendChild(script);
//At this point (or soon after) the code in helper.js will be executed
You would do that in your loading script. My loading script looks something like this:
function importJsFiles(){
const scriptFiles = ["http://somewhere/jquery.js",
"http://somewhere/stuff.js",
"http://somewhere/bootstrapSnippet.js"];
for (let i = 0; i< scriptFiles.length; i++){
importJsFile(scriptFiles[i]);
}
}
function includeSnippet(){
//See https://www.w3schools.com/howto/howto_html_include.asp
//We still have to do it without jQuery,
//because at the time this executes the injected jquery.js
//hasn't run and we do not yet have jQuery available
}
importJsFiles();
//At this point, all script tags are created, but the scripts are NOT loaded yet
includeSnippet();
//At this point, the dom is in its final shape
bootstrapSnippet(); //<- This won't work, because the script tags injected by this code won't be evaluated until after this file/function/whatever returns
//bootstrapSnippet.js
function bootstrapSnippet(){
alert("Snippet JS running")
if (window.jQuery) {
alert("JQuery is avaliable"); //<- This will fire, because the new script
// tags are evaluated in order, and we put the
// jquery.js one before the bootstrapSnippet.js one
}
//So now we CAN do this:
$('#coolIncludable').show();
}
bootstrapSnippet();
There are many more interesting ideas and details in this post that I picked the link above from. I hope someday I'll find time to explore them all.

Ads with script tags in template [Vue.js]

In our project we've previously been using Thymeleaf, but now that we're moving over to Vue.js, we're experiencing some issues using the same ad scripts. The scripts look like this. I've only altered the URLs.
<script data-adfscript="sub.adcompany.net/asdf/?id=256746"></script>
<script src="//sub.adcompany.net/url/to/advertisement/script.js" async="async" defer="defer"></script>
If we put these tags in the <template>, Webpack gives the following message:
Templates should only be responsible for mapping the state to the UI.
Avoid placing tags with side-effects in your templates, such as
, as they will not be parsed.
So I've then been Googling all over to find a similar case. There are some plugins that do this for Google Ads, but they won't work for us. Escaping the script tags <\/script> works in a way, but then the script isn't added to the DOM until after loaded, and so it doesn't run.
Has anyone run into similar issues? If so, what was your solution?
Vue file looks something like this:
<template>
<aside class="sidebar-ad ui-wide">
<script data-adfscript="sub.adcompany.net/asdf/?id=256746"></script>
<script src="//sub.adcompany.net/url/to/advertisement/script.js" async="async" defer="defer"></script>
</aside>
</template>
<script>
export default {
data () {
return {}
}
}
</script>
There is a workaround. Works with style tag too.
<component is="script" src="https://www.example.com/example.js" async></component>
You should not treat Vue templates as a your final HTML, although their syntax is nearly identical and is also HTML-syntax compliant.
Templates are just a UI scaffolds for the data (that is why it is called a data-driven paradigm). They get parsed and transformed into render functions that in the end will produce the final and reactive DOM tree. During this process the <script> tags are indeed ommited cause it is not a place for any logic to happen.
However if you really need to embed any 3rd party script within your component there is a neat way to do this.
First, create container for the script:
<template>
<div id="component-root">
<!-- (...) -->
<div v-el:script-holder></div>
</div>
</template>
Then dynamicly create <script> tag and insert it directly to the DOM tree (using pure Vanilla JS):
<script>
export default {
data () {
return {};
},
ready() {
let scriptEl = document.createElement('script');
scriptEl.setAttribute('src', 'https://cdn.com/somescript.js');
scriptEl.setAttribute('data-some-param', 'paramvalue');
this.$els.scriptHolder.appendChild(scriptEl);
},
}
</script>
The this.$els.scriptHolder returns actual DOM element, calling the appendChild() forces the browser to insert DOM node and run the script just like during ordinary HTML code rendering.
Instead of $els you could also use $el which would return the components root DOM element (in this example the <div id="component-root">) or even the $root.$el which would return the Vue App root DOM element.
Note that this.$els is a Vue 1 feature, which has been replaced with $refs in Vue 2: https://v2.vuejs.org/v2/guide/migration.html#v-el-and-v-ref-replaced
You can try with this package
https://www.npmjs.com/package/vue-script2
I made a vue component to handle script loading within the template tags.
Personally, I use it to load ads in my vuejs apps.
here:
https://github.com/TheDynomike/vue-script-component
I found a way to work around this, not sure how well it works but this the way I did it.
Run the <script> in normal html then go to Dev tools and copy the iframe code made by the script and then paste it into the code as iframe instead of the <script>.
just place your in vue template scripts after your app, in exemple at the end of the body
(function($) {
$('#your-app').find('script').appendTo('body')
<script> your vue script (create app)</script>
})(jQuery);

Setting data attribute for object element in html

i have an object element in my html body to show an Active reports which exports to a .pdf file. I need to use javascript to automatically print the pdf out to the client's default printer and then save the pdf to the server:
<script language="javascript" type="text/javascript">
// <!CDATA[
function PrintPDF() {
pdf.click();
pdf.setActive();
pdf.focus();
pdf.PrintAll();
}
// ]]>
....
<body onload="return PrintPDF();">
<form id="form1" runat="server">
<object id="pdfDoc" type="application/pdf" width="100%" height="100%" data="test.aspx?PrintReport=yes&SavePDF=yes"/>
</form>
</body>
With the data hard-code in the object tag, everything run without a problem.
The problem now is that I need to pass querystring to this page dynamically. I tried to set the attribute data in the javsacript to pass the querystring. The querystring value passed successfully, but the data attribute does not seem to be set. I get a blank page.
pdf.setAttribute("data","test.aspx?PrintReport=yes&SavePDF=yes&AccNum="+AccNum);
Does anyone have a clue how I can set the data attribute dynamically to pass in querystring?
Thanks,
var pdfObj = document.getElementById('pdfDoc');
pdfObj.data="test.aspx?PrintReport=yes&SavePDF=yes&AccNum="+AccNum;
As far as the data attribute you're doing everything fine. Here are some examples:
http://jsfiddle.net/3SxRu/
I think your problem might be more to do with the order of execution. What does your actual code look like? Are you writing over the body onLoad function or something?
Also, I assume using the data attribute is a requirement. HTML5 defines data-*. This attribute isn't really valid. Again, maybe your system requires it.
I suspect that things are happening out of order. Try waiting until the onload event of the window before adding the embed.
Also, I suggest using a script like PDFObject to handle the embedding since it is a reliable way to embed PDF across all the various browsers out there. For example you might have something like the following:
<html>
<head>
<title>PDFObject example</title>
<script type="text/javascript" src="pdfobject.js"></script>
<script type="text/javascript">
window.onload = function (){
// First build the link to the PDF raw data ("bits")
// getQueryStrings assumes something like http://stackoverflow.com/questions/2907482/how-to-get-the-query-string-by-javascript
var queryStrings = getQueryStrings();
var reportNameParamValue = queryStrings["reportName"];
var pdfBitsUrl = "getReportPdfBits.aspx?reportName=" + reportNameParamValue;
// just in case PDF cannot be embedded, we'll fix the fallback link below:
var pdfFallbackLink = document.getElementById("pdfFallbackAnchor");
pdfFallbackLink.href = pdfFallbackLink;
// now perform the actual embed using PDFObject script from http://pdfobject.com
var success = new PDFObject( {
url: pdfBitsUrl;
}).embed();
};
</script>
</head>
<body>
<p>It appears you don't have Adobe Reader or PDF support in this web
browser. <a id="pdfFallbackAnchor" href="sample.pdf">Click here to download the PDF</a></p>
</body>

How to include text file into javascript

Is there any way to load some text from another file into javascript, without server side code?
I was thinking to use another element to hold the text inside some comments, but I don't know how to read it's source code with javascript.
Something like:
<script src="myfile.js"></script>
<script> function readMyText() { ... }</script>
In myfile.js:
/* some text */
You can put anything you want into a script tag if you give it a "type" that's not something the browser understands as meaning "JavaScript":
<script id='Turtle' type='text/poem'>
Turtle, turtle, on the ground;
Pink and shiny - turn around.
</script>
You can get the contents via the "innerHTML" property:
var poemScript = document.getElementById('Turtle');
var poem = poemScript.innerHTML;
Here is a jsfiddle to demonstrate.
That trick is popular lately with people doing client-side page building via templates.
Without using ajax or any server code... sorry mate but you can't :(
Building on Pointy's answer, to import from local files do this:
<script src="foo.txt" id="text" type="text">
</script>
You could also use this to target an external file:
<script src="http://foo.txt"></script>

Relative Paths in Javascript in an external file

So I'm running this javascript, and everything works fine, except the paths to the background image. It works on my local ASP.NET Dev environment, but it does NOT work when deployed to a server in a virtual directory.
This is in an external .js file, folder structure is
Site/Content/style.css
Site/Scripts/myjsfile.js
Site/Images/filters_expand.jpg
Site/Images/filters_colapse.jpg
then this is where the js file is included from
Site/Views/ProductList/Index.aspx
$("#toggle").click(function() {
if (left.width() > 0) {
AnimateNav(left, right, 0);
$(this).css("background", "url('../Images/filters_expand.jpg')");
}
else {
AnimateNav(left, right, 170);
$(this).css("background", "url('../Images/filters_collapse.jpg')");
}
});
I've tried using '/Images/filters_collapse.jpg' and that doesn't work either; however, it seems to work on the server if I use '../../Images/filters_collapse.jpg'.
Basically, I want have the same functionallity as the ASP.NET tilda -- ~.
update
Are paths in external .js files relative to the Page they are included in, or the actual location of the .js file?
JavaScript file paths
When in script, paths are relative to displayed page
to make things easier you can print out a simple js declaration like this and using this variable all across your scripts:
Solution, which was employed on StackOverflow around Feb 2010:
<script type="text/javascript">
var imagePath = 'http://sstatic.net/so/img/';
</script>
If you were visiting this page around 2010 you could just have a look at StackOverflow's html source, you could find this badass one-liner [formatted to 3 lines :) ] in the <head /> section
get the location of your javascript file during run time using jQuery by parsing the DOM for the 'src' attribute that referred it:
var jsFileLocation = $('script[src*=example]').attr('src'); // the js file path
jsFileLocation = jsFileLocation.replace('example.js', ''); // the js folder path
(assuming your javascript file is named 'example.js')
A proper solution is using a css class instead of writing src in js file.
For example instead of using:
$(this).css("background", "url('../Images/filters_collapse.jpg')");
use:
$(this).addClass("xxx");
and in a css file that is loaded in the page write:
.xxx {
background-image:url('../Images/filters_collapse.jpg');
}
Good question.
When in a CSS file, URLs will be relative to the CSS file.
When writing properties using JavaScript, URLs should always be relative to the page (the main resource requested).
There is no tilde functionality built-in in JS that I know of. The usual way would be to define a JavaScript variable specifying the base path:
<script type="text/javascript">
directory_root = "http://www.example.com/resources";
</script>
and to reference that root whenever you assign URLs dynamically.
For the MVC4 app I am working on, I put a script element in _Layout.cshtml and created a global variable for the path required, like so:
<body>
<script>
var templatesPath = "#Url.Content("~/Templates/")";
</script>
<div class="page">
<div id="header">
<span id="title">
</span>
</div>
<div id="main">
#RenderBody()
</div>
<div id="footer">
<span></span>
</div>
</div>
I used pekka's pattern.
I think yet another pattern.
<script src="<% = Url.Content("~/Site/Scripts/myjsfile.js") %>?root=<% = Page.ResolveUrl("~/Site/images") %>">
and parsed querystring in myjsfile.js.
Plugins | jQuery Plugins
Please use the following syntax to enjoy the luxury of asp.net tilda ("~") in javascript
<script src=<%=Page.ResolveUrl("~/MasterPages/assets/js/jquery.js")%>></script>
I found this to work for me.
<script> document.write(unescape('%3Cscript src="' + window.location.protocol + "//" +
window.location.host + "/" + 'js/general.js?ver=2"%3E%3C/script%3E'))</script>
between script tags of course... (I'm not sure why the script tags didn't show up in this post)...
You need to add runat="server" and and to assign an ID for it, then specify the absolute path like this:
<script type="text/javascript" runat="server" id="myID" src="~/js/jquery.jqGrid.js"></script>]
From the codebehind, you can change the src programatically using the ID.
This works well in ASP.NET webforms.
Change the script to
<img src="' + imagePath + 'chevron-large-right-grey.gif" alt="'.....
I have a master page for each directory level and this is in the Page_Init event
Dim vPath As String = ResolveUrl("~/Images/")
Dim SB As New StringBuilder
SB.Append("var imagePath = '" & vPath & "'; ")
ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "LoadImagePath", SB.ToString, True)
Now regardless of whether the application is run locally or deployed you get the correct full path
http://localhost:57387/Images/chevron-large-left-blue.png

Categories