Add Javascript in AMP Pages - javascript

Please help me understand how to add javascript in the AMP (Accelerated Mobile Pages). My requirement is I get an ID in URL. For example localhost:8080/home?id=1.I would like to access that id in my html file.
or else please let me know how to add any javascript file.
Thank you.

Unfortunately, you cannot add arbitrary scripts in AMP. From the specification, under "HTML Tags," for the tag script:
Prohibited unless the type is application/ld+json. (Other non-executable values may be added as needed.) Exception is the mandatory script tag to load the AMP runtime and the script tags to load extended components.
So, if you want to use JavaScript from AMP, you have to use AMP's predefined components. I don't see a component that does what you want.

As of 11th of April 2019 Official Announcement,
it is now possible using your JS inside an AMP project with amp-script component.
First you need to import it to your project:
At the top of your .html file import:
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
Wrap the html element/s with the amp-script component:
<!-- hello-world.html -->
<amp-script layout="container" src="https://yourdomain.com/hello-world.js">
<button id="hello">Insert Hello World!</button>
</amp-script>
Now you can create the JS file
// hello-world.js
const button = document.getElementById('hello');
button.addEventListener('click', () => {
const el = document.createElement('h1');
el.textContent = 'Hello World!';
// `document.body` is effectively the <amp-script> element.
document.body.appendChild(el);
});
You can find more details and how it works in the
AMP git repo amp-script.md

As far as I know, you can add Javascript to AMP by hosting the AMP script on your origin and intercept the request to get the script using a Service Worker. This technique is called "AMP as PWA". Here is the code
function createCompleteResponse (header, body) {
return Promise.all([
header.text(),
getTemplate(RANDOM STUFF AMP DOESN’T LIKE),
body.text()
]).then(html => {
return new Response(html[0] + html[1] + html[2], {
headers: {
'Content-Type': 'text/html'
}
});
});
}
More explanation here: https://www.smashingmagazine.com/2016/12/progressive-web-amps/#amp-as-pwa

Javascript blocks DOM construction and delays page rendering, so AMP allows only asynchronous Javascript - AMP pages can't include any custom Javascript. Interactive page features can be handled in custom AMP elements instead of using Javascript.

Custom javascript is disallowed on AMP pages, and is one of the founding principles of AMP. You can put custom js within an amp-iframe, as long as the amp-iframe is xdomain'd from the main page.

Related

Can I use embedded Javascript in a ghost blog entry?

I am using the Ghost Blogging Software and tumbled over Tangle these days. So it would be great to be able to use that in some of my Blog posts.
I have included the Tangle-files in my theme and load them in default.hbs. However, I cannot find a way to embed some javascript (to be executed, but not displayed) in the markdown document. I would like to write the javascript in the markdown document too as I do not want to add js-Files to my theme each time I write a blog post with reactive content.
Is there a way to do this?
It seems there is. You should be able to include the JavaScript you want to run by just placing it inside of <script> tags like they talk about on this post
Edit: To further explain, a markdown file is just different syntax for creating html tags, but you can use regular html and script tags inside them. To get an script to run per the above link, you should only need to include whatever js you want to run inside of the script tags. This example should work inside of a markdown post file to replicate the Tangle example on the doc page
When you eat <span data-var="cookies" class="TKAdjustableNumber"> cookies</span>,
you consume <span data-var="calories"> calories</span>.
<script>
var tangle = new Tangle(document, {
initialize: function () { this.cookies = 3; },
update: function () { this.calories = this.cookies * 50; }
});
</script>

AngularJS - inject trusted code from loaded content

I've hooked up a lazy loader in Angular. It pulls in full templates and extracts key information from that full template in order to populate a partial. This full page template has script tags which load in and then register with the existing app. All of this works fine. My problem is that I'd like to remove the only use of jQuery in this approach.
The root issue is that the JS inside of something.js doesn't execute when using $element.html(), but it does execute when using $.html(), despite the script tag being placed in the DOM in both approaches.
Working code, including lazy loader and post-bootstrap registration of lazy-loaded JS:
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
// i'd like the following to not rely on full jQuery.
$("#stage").html(partial);
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
I've tried what seems like the logical translation:
$element.html($compile(partial)($scope));
and the DOM is created properly, but the JS inside of the loaded <script> tag doesn't actually execute. My research suggested this was an $sce issue, so I tried:
$element.html($compile($sce.trustAsHtml(partial)($scope));
but i get the same result. the DOM is fine, but the JS doesn't actually execute and so I get undefined controller issues.
I've tried playing with $sce.JS and $sce.RESOURCE_URL but the docs didnt elaborate much so I'm not sure I know whether or not what I'm trying is even right.
I've also tried $element[0].innerHTML but I get the same result as $element.html().
Preemptive disclaimer: I can trust the incoming HTML/JS. I know it's inadvisable. This isn't my baby and it is much more complicated than I explained so please try to stay on topic so other people in this position may not have as hard of a time as I am :)
The $http.get happens in a provider, and the $element.html happens in a directive. I consolidated them to remove noise from the problem.
Jquery will find any script tags and evaluate them (either a direct eval or appending them to the head for linked scripts) when calling html(), see this answer. I'm assuming angular's jquery lite doesn't do this. You would need to effectively replicate what jquery is doing and look for script tags in the html you are appending.
Something like this (although I haven't tested it):
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
var d = document.createElement('div');
d.innerHTML = partial;
var scripts = d.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
document.head.appendChild(scripts[0]);
}
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
This is far from an ideal solution as it gives you no guarantee of when things are loaded and doesn't really handle dependencies across scripts. If you can control the templates it would be simpler to remove the scripts from them and load them independently.

Why adding a <script> tag at runtime doesn't load the javascript file? (with react.js)

I have this react.js script that adds the following code into the html
// returned by the render method
React.DOM.div({
dangerouslySetInnerHTML: {
__html: '<script type="text/javascript" async="" src="//myapp.disqus.com/embed.js"></script>'
}
})
Now my html looks like:
<script type="text/javascript" async="" src="//myapp.disqus.com/embed.js"></script>
Which seems perfect but the problem is that it doesn't load the script.
The script tag is inserted into the middle of the body, nested within some other div tags.
What might be the problem?
Thanks
Rendering the script tag to the page with react isn't the right solution - I coudln't get it to work with JSX, I assume the same applies here. Not sure why, but just add it the plain old javascript way:
var script = document.createElement("script");
script.src = "//myapp.disqus.com/embed.js";
script.async = true;
document.body.appendChild(script);
Put that in the componentWillMount of your root component.
I just added Disqus to my React app yesterday. I used the 'react-disqus-thread' module and had it up and running in no time.
Here it is on github: https://github.com/mzabriskie/react-disqus-thread
And npm: https://www.npmjs.com/package/react-disqus-thread
The component takes the following props:
shortname - This is 'myapp' in //myapp.disqus.com/embed.js
identifier - a unique blogId that can identify your blog post if the url changes
title
url - if you do not provide this, the module will detect the url and provide it.
Which browser you tested ? If you use async="true" in your script tag, it won't block. But that's only supported by a couple of browsers yet .

How do I compose templates on both server and client using dust.js

I'm trying to make a modern site that loads data using ajax, renders it in a template and uses jQuery and pushstate to display it, plus server-side rendering so that initial page loads are fast and spiders can crawl as intended. Of course, I read the article about linkedin using dust.js to achieve this.
Now, dust.js claims the following advantages:
Composable: Designers should be able to break presentation markup into manageable components and combine these components at runtime. It should not be necessary to statically link templates or manually assemble 'layouts' inside application code.
Format agnostic: While HTML generation and DOM manipulation are useful in specific instances, a general-purpose template system should not be tied to a particular output format.
So it sounds great, but how do I actually achieve what I want? I've made templates that render a complete page just fine on the server, but they use blocks and inline partials - which means the internal bits can't be rendered at all without the wrapper present (it just returns an error that says it can't find the wrapper template). I don't see how composing inside application code (as opposed to selling point #1 above) can be avoided on the client.
I don't understand what #2 above even means. I guess it means that you get the output as a string and can do whatever you want with it?
The documentation is about as clear as mud.
So what do I do? Is there a better option than dust.js these days? Do I write templates so that they must be composed in application code? If so, by what mechanism do I compose them in application code?
Ok, since there has been trouble understanding my question (which is itself understandable), I just threw together this (untested) example showing the problem:
Wrapper template:
<html>
<head><title>{+title/}</title>
{+styles/}
</head>
<body>
header header header
<div id="pagecontent">{+content/}</div>
footer footer footer
<script src="jquery"></script>
<script src="dust"></script>
<script src="see 'Client side script' below"></script>
</body>
</html>
'time' template:
{>wrap/}
{<title}Time in millis{/title}
{<styles}
<style>
body {
background-color: {bgcolor};
}
</style>
{/styles}
{<content}
The time: {time}<br />
Switch format
{/content}
Server side code:
app.get('/millis',function(req,res) {
res.render('time',{millis:new Date().getTime(),link:'/time',bgcolor:'lightgreen'});
}
app.get('/time',function(req,res) {
res.render('time',{millis:new Date().toString(),link:'/millis',bgcolor:'lightpink'});
}
So, the server will render the page fine, but what about the client? Read on.
Client side script:
//load the 'time' template into dust somewhere up here
$(function(){
$('a').click(function(e){
var newcontent;
switch($(this).attr('href')) {
case '/time':
//FAILS: can't find the wrapper. We need logic to get the title and styles from the template and fill it in in-place in the DOM
newcontent = dust.render('time',{millis:new Date().toString(),link:'/millis',bgcolor:'lightpink'});
case '/millis':
//FAILS: can't find the wrapper. We need logic to get the title and styles from the template and fill it in in-place in the DOM
newcontent = dust.render('time',{millis:new Date().getTime(),link:'/time',bgcolor:'lightgreen'});
default: return;
}
e.preventDefault();
$('#pagecontent').fadeOut(1000,function(){
//use pushstate and stuff here
$(this).html(newcontent);
$(this.fadeIn(1000);
});
});
});
I was wondering the same thing and came across this. You may have seen this too, but I'm leaving this here in case it helps others.
http://spalatnik.com/blog/?p=54
I haven't implemented this so my deductions below are based off the above article and some (hopefully) educated assumptions. I'd certainly like to continue the discussion if the following is incorrect, as I'm learning as well.
I suspect that you'd have two types of wrapper templates. One is the wrapper template that you provided above (for server side rendering). A second wrapper template would be quite different (as you see below). I'm copying verbatim from the blog above in the following example. I presume that all your compiled DustJS templates are in the file dust-full-0.3.0-min.js below.
<html>
<head>
<script src="dust-full-0.3.0.min.js"></script>
<script type="text/javascript">
//example showing client-side compiling and rendering
var compiled = dust.compile("Hello {name}!", "index");
dust.loadSource(compiled);
dust.render("index", {name: "David"}, function(err, out) {
if(err != null)
alert("Error loading page");
//assume we have jquery
$("#pageContainer").html(out);
});
</script>
</head>
<body>
<div id="pageContainer"></div>
</body>
</html>
I suspect that in your Express server, you'd check the User Agent and decide on which template to render out. If it's a bot user-agent, use server-side generation in your example. Otherwise, use the client side template above.

How to handle localization in JavaScript files?

I want JavaScript code to be separated from views.
I got the requirement to implement localization for a simple image button generated by JavaScript:
<img src="..." onclick="..." title="Close" />
What's the best technique to localize the title of it?
PS: I found a solution by Ayende. This is the right direction.
Edit:
I got Localization helper class which provides the Controller.Resource('foo') extension method.
I am thinking about to extend it (helper) so it could return all JavaScript resources (from "ClientSideResources" subfolder in App_LocalResources) for the specified controller by its name. Then - call it in BaseController, add it to ViewData and render it in Layout.
Would that be a good idea?
EDIT
Consider writing the necessary localized resources to a JavaScript object (hash) and then using it for lookup for your dynamically created objects. I think this is better than going back to the server for translations. This is similar to adding it via viewdata, but may be a little more flexible. FWIW, I could consider the localization resources to be part of the View, not part of the controller.
In the View:
<script type="text/javascript"
src='<%= Url.Content( "~/Resources/Load?translate=Close,Open" %>'></script>
which would output something like:
var local = {};
local.Close = "Close";
local.Open = "Open";
Without arguments it would output the entire translation hash. Using arguments gives you the ability to customize it per view.
You would then use it in your JavaScript files like:
$(function(){
$('#button').click( function() {
$("<img src=... title='" + local.Close + "' />")
.appendTo("#someDiv")
.click( function() { ... } );
});
});
Actually, I'm not too fussed about keeping my JavaScript code out of my views as long as the JavaScript code is localized in a container. Typically I'll set my master page up with 4 content area: title, header, main, and scripts. Title, header, and main go where you would expect and the scripts area goes at the bottom of the body.
I put all my JavaScript includes, including any for viewusercontrols, into the scripts container. View-specific JavaScript code comes after the includes. I refactor shared code back to scripts as needed. I've thought about using a controller method to collate script includes, that is, include multiple scripts using a single request, but haven't gotten around to that, yet.
This has the advantage of keeping the JavaScript code separate for readability, but also allows me to easily inject model or view data into the JavaScript code as needed.
Actually ASP.NET Ajax has a built-in localization mechanism: Understanding ASP.NET AJAX Localization
If you insist on keeping it separate, you could do something like:
//keep all of your localised vars somewhere
var title = '{title_from_server}';
document.getElementById('someImage').title = title;
Remember, if you use JavaScript code to initialize any text of elements, your site will degrade horribly where JavaScript isn't available.

Categories

Resources