#BlackLivesMatter
AMP
  • websites

amp-script

Introduction

The amp-script component allows you to run custom JavaScript. Your code runs in a Web Worker, and certain restrictions apply.

Setup

First, you need to import the amp-script extension.

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

For inline scripts, you need to generate a script hash. Use the data-ampdevmode attribute to disable this requirement during development. Visit the documentation to learn more.

<meta name="amp-script-src" content="sha384-iER2Cy-P1498h1B-1f3ngpVEa9NG1xIxKqg0rNkRX0e7p5s0GYdit1MRKsELIQe8 sha384-UPY0FmlOzIjSqWqMgbuaEbqIdvpGY_FzCuTAyoLdrFJb2NYf8cPWJlugA0rUbXjL

Loading a script from a URL

To load your script from a URL, use the src attribute. This example loads and runs a script called hello.js. Valid AMP requires all URLs to be absolute and use https.

Here's the script in hello-world.js:

const button = document.getElementById('hello-url');

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

And here's the HTML:

<amp-script layout="container"
  src="https://amp.dev/documentation/examples/components/amp-script/hello-world.js"
  class="sample">
  <button id="hello-url">Say hello!</button>
</amp-script>
Dieses Snippet im Playground öffnen

Using an inline script

You can also include a script inline and reference it by id. Note that, on the script, you need to set type=text/plain and target=amp-script.

<amp-script layout="container"
  script="hello-world"
  class="sample">
  <button id="hello-inline">Say hello!</button>
</amp-script>

<script id="hello-world" type="text/plain" target="amp-script">
    const button = document.getElementById('hello-inline');

    button.addEventListener('click', () => {
      const h1 = document.createElement('h1');
      h1.textContent = 'Hello World!';
      document.body.appendChild(h1);
    });
  </script>
Dieses Snippet im Playground öffnen

amp-script passes its children to your script as the virtual DOM - not the entire DOM. To your script, those children are the DOM.

Thus, document.body refers to what's inside the amp-script tag, not the actual body. document.body.appendChild(...) actually adds an element inside the amp-script element.

Using the fetch API

amp-script supports the fetch API. amp-script allows us to update the page on load if it knows that the script can't change the component's height. Here, we use fixed-height layout, and we specify the height in an HTML attribute. See the documentation for details.

The time at page load was:
<amp-script layout="fixed-height"
  height="36"
  script="time-script"
  class="sample">
  <div>The time at page load was: <span id="time"
      class="answer-text"></span></div>
</amp-script>

<script id="time-script" type="text/plain" target="amp-script">
    const fetchCurrentTime = async () => {
      const response = await fetch('https://amp.dev/documentation/examples/api/time');
      const data = await response.json();
      const span = document.getElementById('time');
      span.textContent = data.time;
    }

    fetchCurrentTime();
  </script>
Dieses Snippet im Playground öffnen

Multiple fetches

In a container whose size can change, your code can make DOM changes until 5 seconds after the final fetch() completes. This example makes multiple calls to a slow API. It displays the result from each call when it returns.

<amp-script layout="container"
  script="multi-fetch-script"
  class="sample">
  <button id="multi-fetch">How slow is our API?</button>
</amp-script>
<script id="multi-fetch-script" type="text/plain" target="amp-script">
    const randomTime = () =>  Math.floor(Math.random() * 10) + 5;
    const button = document.getElementById('multi-fetch');
    function tripleFetch() {
      for (let i =0; i < 3; i++) {
        fetch('https://amp.dev/documentation/examples/api/slow-text?delay=' + randomTime())
          .then(response => response.text())
          .then(insertText);
      }
    }
    function insertText(text) {
      const div = document.createElement('div');
      div.textContent = text;
      document.body.appendChild(div);
    }
    button.addEventListener('click', tripleFetch);
  </script>
Dieses Snippet im Playground öffnen

Using a WebSocket for live updates

amp-script supports WebSockets. This example simulates a live blog.

<amp-script layout="fixed-height"
  height="200"
  script="live-blog-script"
  class="sample"
  sandbox="allow-forms">
  <button id="live-blog-start">Start live blog</button>
  <div id="live-blog-area"></div>
</amp-script>
<script id="live-blog-script" type="text/plain" target="amp-script">
    const button = document.getElementById('live-blog-start');
    const blogDiv = document.getElementById('live-blog-area');
    button.addEventListener("click", () => {
      button.setAttribute('disabled', '');
      button.textContent = 'Live blog begun';
      const socket = new WebSocket('wss://amp.dev/documentation/examples/api/socket/live-blog');
      socket.onmessage = event => {
        let newDiv = document.createElement('div');
        let time = new Date().toLocaleTimeString();
        newDiv.innerHTML = `<span class="time">${time}: </span><span>${event.data}</span>`;
        blogDiv.appendChild(newDiv);
      };        
    });      
  </script>
Dieses Snippet im Playground öffnen

Showing live data

You can also use setInterval() or setTimeout to get fresh data.

The current time is:
<amp-script layout="fixed-height"
  height="36"
  script="live-time-script"
  class="sample">
  <div>The current time is: <span id="live-time"
      class="answer-text"></span></div>
</amp-script>

<script id="live-time-script" type="text/plain" target="amp-script">
    const span = document.getElementById('live-time');

    const fetchCurrentTime = async () => {
      const response = await fetch('https://amp.dev/documentation/examples/api/time');
      const data = await response.json();
      span.textContent = data.time;
    }

    setInterval(fetchCurrentTime, 1000);
  </script>
Dieses Snippet im Playground öffnen

Custom form validation

You can also use amp-script to implement custom form validation. This script enables the button when the input field contains only capital letters.

<amp-script layout="container"
  script="form-validation-script"
  sandbox="allow-forms"
  class="sample">
  <input id="validated-input"
    placeholder="Only uppercase letters allowed...">
  <button id="validated-input-submit"
    disabled>Submit</button>
</amp-script>

<script id="form-validation-script" type="text/plain" target="amp-script">
    const submitButton = document.getElementById('validated-input-submit');
    const validatedInput = document.getElementById('validated-input');

    function allUpper() {
      let isValid = /^[A-Z]+$/.test(validatedInput.value);

      if (isValid) {
        submitButton.removeAttribute('disabled');
      } else {
        submitButton.setAttribute('disabled', '');
      }
    }

    validatedInput.addEventListener('input', allUpper);
  </script>
Dieses Snippet im Playground öffnen

Detecting the operating system

Your script has access to global objects like navigator. This script uses this object to try to guess your device's operating system.

Your operating system is:
<amp-script layout="fixed-height"
  height="36"
  script="user-agent-script"
  class="sample">
  <div>
    Your operating system is:
    <span id="operating-system"
      class="answer-text"></span>
  </div>
</amp-script>

<script id="user-agent-script" type="text/plain" target="amp-script">
    // Adapted with gratitude from https://stackoverflow.com/a/38241481

    function getOS() {
      const userAgent = navigator.userAgent,
            platform = navigator.platform,
            macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
            windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
            iosPlatforms = ['iPhone', 'iPad', 'iPod'];
      if (macosPlatforms.includes(platform)) {
        return 'Mac OS';
      } else if (iosPlatforms.includes(platform)) {
        return 'iOS';
      } else if (windowsPlatforms.includes(platform)) {
        return 'Windows';
      } else if (/Android/.test(userAgent)) {
        return 'Android';
      } else if (/Linux/.test(platform)) {
        return 'Linux';
      }
      return 'Unknown';
    }

    const span = document.getElementById('operating-system');
    span.textContent = getOS();
  </script>
Dieses Snippet im Playground öffnen

Personalization

Similarly, you can use the navigator object, or other means, to personalize content for your user. The following script detects the browser's language and displays a localized greeting.

<amp-script layout="fixed-height"
  height="40"
  script="translation-script"
  class="sample">
  <h2 id="translated-greeting"></h2>
</amp-script>
<script id="translation-script" type="text/plain" target="amp-script">
    const translationMap = {
      'en': 'Hello',
      'fr': 'Bonjour',
      'es': 'Hola',
      'hi': 'हैलो',
      'zh': '你好',
      'pr': 'Olá'
    };
    const lang = navigator.language.slice(0, 2);
    const translation = translationMap[lang];
    if (!translation) {
      translation = "Couldn't recognize your language. So: Saluton";
    }

    let greeting = document.getElementById('translated-greeting');
    greeting.innerHTML = translation + '!';
  </script>
Dieses Snippet im Playground öffnen

Interacting with <amp-state>

Your script can use state variables and binding to affect the area outside the <amp-script> component. Here, when a button is clicked, we set a state variable's value to an image URL. That state variable is bound to the src attribute of an <amp-img>.

<amp-img layout="responsive"
  height="426"
  width="640"
  src="https://amp.dev/static/samples/img/product1_640x426.jpg"
  [src]="imgSrc"></amp-img>
<amp-script layout="container"
  script="state-script"
  class="sample">
  <button id="apple-button"
    class="fruit-button">Apple</button>
  <button id="orange-button"
    class="fruit-button">Orange</button>
</amp-script>

<script id="state-script" type="text/plain" target="amp-script">
    const appleButton = document.getElementById('apple-button');
    const orangeButton = document.getElementById('orange-button');
    const path = 'https://amp.dev/static/samples/img/';

    appleButton.addEventListener(
      'click',
      () => AMP.setState({imgSrc: path + 'product1_640x426.jpg'})
    );

    orangeButton.addEventListener(
      'click',
      () => AMP.setState({imgSrc: path + 'product2_640x426.jpg'})
    );
  </script>
Dieses Snippet im Playground öffnen

Interacting with AMP components

Your script can use state variables and binding to communicate with an AMP component. Bind an attribute in the component to an expression containing a state variable. When your script modifies that state variable, the change will propagate to the component. Similarly, if an AMP component changes the state variable's value, your script can get the new value.

<div id="carousel-sample">
  <amp-carousel type="slides"
    layout="responsive"
    width="450"
    height="300"
    controls
    loop
    [slide]="slideIndex"
    on="slideChange: AMP.setState({slideIndex: event.index})">
    <amp-img src="https://amp.dev/static/inline-examples/images/image1.jpg"
      layout="responsive"
      width="450"
      height="300"></amp-img>
    <amp-img src="https://amp.dev/static/inline-examples/images/image2.jpg"
      layout="responsive"
      width="450"
      height="300"></amp-img>
    <amp-img src="https://amp.dev/static/inline-examples/images/image3.jpg"
      layout="responsive"
      width="450"
      height="300"></amp-img>
  </amp-carousel>
  <p>Slide <span [text]="slideIndex + 1">1</span> of 3</p>
  <amp-script layout="container"
    script="carousel-script"
    class="sample">
    <button id="carousel-button"
      class="fruit-button">Surprise me</button>
  </amp-script>

  <script id="carousel-script" type="text/plain" target="amp-script">
      const button = document.getElementById('carousel-button');

      function gotoRandomSlide() {
        const slide = Math.floor(Math.random() * 3);
        AMP.setState({slideIndex: slide});
      }

      button.addEventListener('click', gotoRandomSlide);
    </script>
</div>
Dieses Snippet im Playground öffnen
Wünschst du eine genauere Erklärung?

Sollten die Erklärungen auf dieser Seite nicht all deine Fragen beantworten, kannst du dich gerne an andere AMP Nutzer wenden, um deinen konkreten Use Case zu besprechen.

Zu Stack Overflow wechseln
Ein Feature wurde nicht erklärt?

Das AMP Projekt ist auf deine Teilnahme und deine Beiträge angewiesen! Wir hoffen natürlich, dass du dich aktiv an unserer Open Source Community beteiligen wirst. Aber wir freuen uns auch über einmalige Beiträge zu den Themen, die dich besonders interessieren.

Beispiel auf GitHub bearbeiten