Search code examples
javascriptscrollamp-html

AMP: amp-script is not running


I am trying to use amp-script to toggle a class on my nav (top menubar) when the window starts to scroll. The effect is to turn the fixed nav from semi-transparent to solid white.

Here is what I tried ...

<!doctype html>
<html ⚡>
<head>
  <meta charset="utf-8">
  <title>My AMP Page</title>
  <link rel="canonical" href="self.html" />
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
  <script async src="https://cdn.ampproject.org/v0.js"></script>
  <script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
  <style amp-custom>
    nav {
      position: fixed;
      top: 0;
      width: 100vw;
      color: white;
      background-color: rgba(0, 0, 0, 0.2);
      z-index: 10;
      text-align: center;
    }

    .opaque {
      background-color: white;
      color: black;
    }

    .hero {
      height: max(25vh, 250px);
      width: 100vw;
      background: red;
    }

    .spacer {
      height: 4em;
      background: blue;
      margin: 0.5em;
    }
  </style>
  <meta name="amp-script-src" content="sha384-bpf6fazoofAb6S1aK0_hOIa1g6nDaC_SPbTuSJsMxEzAPc11oKjAnJZmeFAMKc2p ">
</head>
<body>
  <script id="scroll-listener" type="text/plain" target="amp-script">
    document.addEventListener("scroll", myFunction);

    function myFunction() {
      console.log('scrolled!!');
      if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
        document.getElementById("topnav").className = "opaque";
      } else {
        document.getElementById("topnav").className = "";
      }
    }
  </script>
  <nav id="topnav">
    <h1>ACME Inc.</h1>
  </nav>
  <main>
    <header class="hero"></header>
    <article>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
      <div class="spacer"></div>
    </article>
  </main>
</body>
</html>

Unfortunately the script has no effect at all. I've also placed console.log statements to see if I get any scroll events reported at all - and nothing shows up in the console.

Any idea what I'm doing wrong here?

UPDATE: Just to make sure that this works as a "non-AMP" page, I changed the line in the example above:

<script id="scroll-listener" type="text/plain" target="amp-script">...</script>

to this ...

<script id="scroll-listener" type="text/javascript">...</script>

and the site works as expected. So I know for sure that the javascript as written does what I want it to do. The issue is entirely with the way AMP is handling the script.


Solution

  • Apparently, you don't understand how amp-script works, which is strange, because there are examples in the official documentation.

    Try this example:

    <!DOCTYPE html>
    <html ⚡>
      <head>
        <meta charset="utf-8" />
        <title>My AMP Page</title>
        <link rel="canonical" href="self.html" />
        <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
        <style amp-boilerplate>
          body {
            -webkit-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            -moz-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            -ms-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            animation: -amp-start 8s steps(1, end) 0s 1 normal both;
          }
          @-webkit-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-moz-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-ms-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-o-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
        </style>
        <noscript
          ><style amp-boilerplate>
            body {
              -webkit-animation: none;
              -moz-animation: none;
              -ms-animation: none;
              animation: none;
            }
          </style></noscript
        >
        <script async src="https://cdn.ampproject.org/v0.js"></script>
        <script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
        <script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
        <meta name="amp-script-src" content="sha384-nPCFvs2yYOVX1DFcHvkEUY1aC6PdG8FAgPUG16rZwxfQJOPh_pM1rA-oUzDsg8pk" />
        <style amp-custom>
          nav {
            position: fixed;
            top: 0;
            width: 100vw;
            color: white;
            background-color: rgba(0, 0, 0, 0.2);
            z-index: 10;
            text-align: center;
          }
    
          .opaque {
            background-color: white;
            color: black;
          }
    
          .hero {
            height: max(25vh, 250px);
            width: 100vw;
            background: red;
          }
    
          .spacer {
            height: 4em;
            background: blue;
            margin: 0.5em;
          }
        </style>
      </head>
      <body>
        <amp-script script="scroll-listener">
          <nav id="topnav">
            <h1>ACME Inc.</h1>
          </nav>
          <main id="main">
            <header class="hero"></header>
            <article>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
              <div class="spacer"></div>
            </article>
          </main>
        </amp-script>
    
        <script id="scroll-listener" type="text/plain" target="amp-script">
          function myFunction(event) {
            console.log('scrolled!!');
          }
          window.addEventListener("scroll", myFunction);
        </script>
      </body>
    </html>

    The example above probably won't help you do what you want, but at least now you'll understand how amp-script works.

    amp-script often requires a custom event to be triggered and will not work immediately after the page loads. Read about it here: https://amp.dev/documentation/components/amp-script/?format=websites#user-gestures

    To complete this task, I recommend that you use the amp-position-observer and amp-animation components. Here is an example:

    <!DOCTYPE html>
    <html ⚡>
      <head>
        <meta charset="utf-8" />
        <title>My AMP Page</title>
        <link rel="canonical" href="self.html" />
        <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
        <style amp-boilerplate>
          body {
            -webkit-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            -moz-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            -ms-animation: -amp-start 8s steps(1, end) 0s 1 normal both;
            animation: -amp-start 8s steps(1, end) 0s 1 normal both;
          }
          @-webkit-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-moz-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-ms-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @-o-keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
          @keyframes -amp-start {
            from {
              visibility: hidden;
            }
            to {
              visibility: visible;
            }
          }
        </style>
        <noscript
          ><style amp-boilerplate>
            body {
              -webkit-animation: none;
              -moz-animation: none;
              -ms-animation: none;
              animation: none;
            }
          </style></noscript
        >
        <script async src="https://cdn.ampproject.org/v0.js"></script>
        <script async custom-element="amp-position-observer" src="https://cdn.ampproject.org/v0/amp-position-observer-0.1.js"></script>
        <script async custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js"></script>
    
        <style amp-custom>
          nav {
            position: fixed;
            top: 0;
            width: 100vw;
            color: cyan;
            background-color: rgba(0, 0, 0, 0.2);
            z-index: 10;
            text-align: center;
          }
    
          h1 {
            position: relative;
            z-index: 2;
          }
    
          .opaque {
            background-color: white;
            color: black;
          }
    
          .hero {
            height: max(25vh, 250px);
            width: 100vw;
            background: red;
          }
    
          .spacer {
            height: 4em;
            background: blue;
            margin: 0.5em;
          }
    
          .scroll-effect {
            background-color: #fff;
            height: 100%;
            left: 0;
            opacity: 0;
            position: absolute;
            top: 0;
            width: 100%;
          }
    
          .top-anchor {
            position: relative;
          }
        </style>
      </head>
      <body>
        <div class="top-anchor">
          <amp-position-observer on="enter:hideAnim.start; exit:showAnim.start" layout="nodisplay"></amp-position-observer>
        </div>
        <nav id="topnav">
          <span class="scroll-effect" id="scroll-effect"></span>
          <h1>ACME Inc.</h1>
        </nav>
        <main id="main">
          <header class="hero"></header>
          <article>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <div class="spacer"></div>
          </article>
        </main>
    
        <amp-animation id="showAnim" layout="nodisplay">
          <script type="application/json">
            {
              "duration": "200ms",
              "fill": "both",
              "iterations": "1",
              "direction": "alternate",
              "animations": [
                {
                  "selector": "#scroll-effect",
                  "keyframes": [
                    {
                      "opacity": "1",
                      "visibility": "visible"
                    }
                  ]
                }
              ]
            }
          </script>
        </amp-animation>
        <amp-animation id="hideAnim" layout="nodisplay">
          <script type="application/json">
            {
              "duration": "200ms",
              "fill": "both",
              "iterations": "1",
              "direction": "alternate",
              "animations": [
                {
                  "selector": "#scroll-effect",
                  "keyframes": [{ "opacity": "0", "visibility": "hidden" }]
                }
              ]
            }
          </script>
        </amp-animation>
      </body>
    </html>