AMP

amp-script

Description

Runs custom JavaScript in a Web Worker.

 

Required Scripts

<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>

Usage

The amp-script component allows you to run custom JavaScript. To maintain AMP's performance guarantees, your code runs in a Web Worker, and certain restrictions apply.

Virtual DOM

Your JavaScript can access the area of the page wrapped within the <amp-script> component. amp-script copies the component's children to a virtual DOM. Your code can access that virtual DOM as document.body.

For example, this <amp-script> component defines a DOM consisting of a single <p>.

<amp-script src="http://example.com/my-script.js" width="300" height="100">
  <p>A single line of text</p>
</amp-script>

If your code appends an element to document.body:

// my-script.js
const p = document.createElement('p');
p.textContent = 'A second line of text';
document.body.appendChild(p);

the new element will be placed after that <p>.

<amp-script src="http://example.com/my-script.js" width="300" height="100">
  <p>A single line of text</p>
  <p>A second line of text</p>
</amp-script>

Loading JavaScript

An amp-script element can load JavaScript in two ways:

  • remotely, from a URL
  • locally, from a <script> element on the page

From a remote URL

Use the src attribute to load JavaScript from a URL:

<amp-script layout="container" src="https://example.com/hello-world.js">
  <button>Hello amp-script!</button>
</amp-script>

From a local element

You can also include your JavaScript inline, in a script tag. You must:

  • Set the script attribute of your amp-script to the local script element's id.
  • Include target="amp-script" in your amp-script.
  • Include type="text/plain" in your script. This way, the browser won't execute your script, allowing amp-script to control it.
<!-- To use inline JavaScript, you must add a script hash to the document head. -->
<head>
  <meta
    name="amp-script-src"
    content="sha384-YCFs8k-ouELcBTgzKzNAujZFxygwiqimSqKK7JqeKaGNflwDxaC3g2toj7s_kxWG"
  />
</head>

...

<amp-script width="200" height="100" script="hello-world">
  <button>Hello amp-script!</button>
</amp-script>

<!-- Add [target="amp-script"] to the <script> element. -->
<script id="hello-world" type="text/plain" target="amp-script">
  const btn = document.querySelector('button');
  btn.addEventListener('click', () => {
    document.body.textContent = 'Hello World!';
  });
</script>

For security reasons, amp-script elements with a script or cross-origin src attribute require a script hash in a <meta name="amp-script-src" content="..."> tag. Also, same-origin src files must have Content-Type: application/javascript or text/javascript.

If your page contains multiple amp-script elements, each requiring a script hash, include each as a whitespace-delimited list in a single <meta name="amp-script-src" content="..."> tag (see examples/amp-script/example.amp.html for an example with multiple script hashes).

How does it work?

amp-script runs your custom JavaScript in a Web Worker. Normally Web Workers don't have access to the DOM. But amp-script gives your code access to a virtual DOM. When your JavaScript modifies this virtual DOM, amp-script updates the real DOM.

Under the hood, amp-script uses @ampproject/worker-dom. For design details, see the "Intent to Implement" issue.

Capabilities

Supported APIs

DOM elements and their properties are generally supported, with a few limits. For example, your code can't add a new <script> or <style> tag to the DOM.

amp-script recreates many commonly used DOM APIs and makes them available to your code. This "hello world" example uses getElementById(), addEventListener(), createElement(), textContent, and appendChild():

const button = document.getElementById('textarea');

button.addEventListener('click', () => {
  const h1 = document.createElement('h1');
  h1.textContent = 'Hello World!';
  document.body.appendChild(h1);
});

Supported DOM APIs include:

  • Element getters like getElementByName(), getElementsByClassName(), getElementsByTagName(), childNodes(), parentNode(), and lastChild()
  • Mutators like createTextNode(), appendChild(), insertBefore(), removeChild(), and replaceChild()
  • Methods involving events like addEventListener(), removeEventListener(), and createEvent()
  • Property and attribute getters like getAttribute(), hasAttribute()
  • Event properties like Event.target, Event.type, and Event.bubbles
  • Element properties like attributes, id,