AMP

使用自定义 JavaScript 创建 UI 小部件

重要提示:此文档不适用于您当前选择的格式 ads

在本教程中,您将学习如何使用 <amp-script>,这是一个允许开发人员在 AMP 中编写自定义 JavaScript 的组件。您将使用它来构建一个小部件,该小部件会检查密码输入字段的内容,仅允许在满足某些要求时提交。AMP 已经使用 <amp-form> 提供了此功能,但 <amp-script> 将使您能够创建自定义体验。

你需要什么

  • 现代网络浏览器
  • HTML、CSS 和 JavaScript 的基本知识
  • 两者之一

背景

AMP 旨在使网站对用户来说更快更稳定。过多的 JavaScript 会使网页变慢。但有时您需要创建 AMP 组件不提供的功能。在这种情况下,您可以使用 <amp-script> 组件来编写自定义 JavaScript。

让我们开始吧!

开始使用

要获取起始代码,请下载或克隆此 github 存储库。完成此操作后,cd 进入您创建的目录。您将看到两个目录:starter_codefinished_codefinished_code 包含您在本教程中将创建的内容。所以我们现在先不看它。相反,cd 进入 starter_code。这包含一个使用 <amp-form> 单独实现我们表单的网页,没有来自 <amp-script> 的帮助。

要执行此练习,您需要在计算机上运行一个网络服务器。如果您已经在这样做,那么一切都准备就绪!如果是这样,根据您的设置,您可以通过在浏览器中输入类似 https://127.0.0.1/amp-script-tutorial/starter_code/index.html 的 URL 来访问起始网页。

或者,您可以使用 serve(一个基于 Node.js 的静态内容服务器)之类的东西来设置一个快速本地服务器。如果您没有安装 Node.js,请在此处下载。安装 Node 后,在命令行中键入 npx serve。然后您可以在这里访问您的网站

https://127.0.0.1:5000/

您也可以自由使用像 GlitchCodePen 这样的在线游乐场。这个包含与 github 存储库相同的代码,如果您愿意,可以从那里开始!

完成此操作后,您将看到我们的起始网页

在您最喜欢的代码编辑器中打开 starter_code/index.html。查看此表单的 HTML。请注意,密码 <input> 包含此属性

on="tap:rules.show; input-debounced:rules.show"

这告诉 AMP 在用户点击或单击密码 <input> 时以及在他们在其中输入任何字符后显示规则 <div>。我们更喜欢使用 focus 事件,这也将涵盖用户选项卡进入输入的情况。至少在编写本教程时,AMP 不会传递此事件,因此我们没有此选项。别担心。我们即将使用 <amp-script> 解决这个问题!

密码 <input> 包含另一个有趣的属性

pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-z\d]).{8,}$"

这个正则表达式组合了一组较小的正则表达式,每个正则表达式都表达了我们的一个验证规则。AMP 在输入内容匹配之前不会让表单提交。如果用户尝试提交,他们会看到一个提供很少详细信息的错误消息

由于我们为您提供的代码不包含处理表单提交的网络服务,因此提交表单不会做任何有用的事情。当然,欢迎您将此功能添加到您自己的代码中!

这种体验是可以接受的 - 但不幸的是,AMP 无法解释我们哪些验证规则失败了。它无法知道,因为我们必须将规则压缩到一个正则表达式中。

现在,让我们使用 <amp-script> 来构建更用户友好的体验!

使用 <amp-script> 重建

要使用 <amp-script>,我们需要导入它自己的 JavaScript。打开 index.html 并将以下内容添加到 <head> 中。

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

<amp-script> 允许我们将自己的 JavaScript 内联或在外部文件中编写。在本练习中,我们将编写足够的代码来证明一个单独的文件是合理的。创建一个名为 js 的新目录,并向其中添加一个名为 validate.js 的新文件。

<amp-script> 允许您的 JavaScript 操作其 DOM 子项 - 组件封闭的元素。它将这些 DOM 子项复制到虚拟 DOM 中,并使您的代码可以访问此虚拟 DOM。在本练习中,我们希望我们的 JavaScript 控制我们的 <form> 及其内容。因此,我们将把 <form> 包裹在 <amp-script> 组件中,如下所示

<amp-script src="js/validate.js" layout="fixed" sandbox="allow-forms" height="500" width="750">
  <form method="post" action-xhr="#" target="_top" class="card">
    ...
  </form>
</amp-script>

我们的 <amp-script> 包含属性 sandbox="allow-forms"。这告诉 AMP,脚本可以修改表单的内容。

由于 AMP 旨在保证快速、视觉稳定的用户体验,因此它不会让我们的 JavaScript 随时对 DOM 进行无限制的更改。如果 <amp-script> 组件的大小无法更改,您的 JavaScript 可以进行更多更改。它还允许在用户交互后进行更实质性的更改。您可以在 参考文档中找到详细信息。对于本教程,只需知道我们已指定一个不是 containerlayout 类型,并且我们使用了 HTML 属性来锁定组件的大小。这意味着任何 DOM 操作都限制在页面的特定区域内。

如果您使用的是 AMP 验证器 Chrome 扩展程序,您现在将看到一条错误消息

如果您没有此扩展程序,请将 #development=1 附加到您的 URL,AMP 会将验证错误输出到您的控制台。

这是什么意思?如果您的 <amp-script> 从外部文件加载其 JavaScript,AMP 要求您指定绝对 URL。我们可以通过使用 https://127.0.0.1/js/validate.js 来解决此问题。但 AMP 也要求使用 HTTPS。因此,我们仍然会收到验证错误,并且在我们的本地网络服务器上设置 SSL 超出了本教程的范围。如果您想这样做,可以按照这篇文章中的说明进行操作。

接下来,我们可以从表单中删除 pattern 属性及其正则表达式:我们不再需要它了!

我们还要删除当前用于告诉 AMP 显示我们密码规则的 on 属性。正如上面预示的那样,我们将改为使用 <amp-script> 来捕获浏览器的 focus 事件。

pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-z\d]).{8,}$"
on="tap:rules.show; input-debounced:rules.show"

现在,让我们确保我们的 <amp-script> 正在工作。打开您创建的 validate.js 文件并添加一个调试消息

console.log("Hello, amp-script!");

转到您的浏览器,打开控制台,然后重新加载页面。确保您看到您的消息!

我的 JavaScript 在哪里?

<amp-script> 在 Web Worker 中运行您的 JavaScript。Web Worker 无法直接访问 DOM,因此 <amp-script> 使 Worker 可以访问 DOM 的虚拟副本,该副本与真实 DOM 保持同步。<amp-script> 提供了许多常见 DOM API 的模拟,您几乎可以在 JavaScript 中以通常的方式使用所有这些 API。

如果任何时候您需要调试您的脚本,您可以像对待任何 JavaScript 一样在 Web Worker 中的 JavaScript 中设置断点。您只需要知道在哪里找到它。

在 Chrome DevTools 中,打开“Sources”选项卡。在底部,您将看到一个长十六进制字符串,如下所示。展开该字符串,然后展开“no domain”区域,您将看到您的脚本

添加我们的 JavaScript

既然我们知道我们的 <amp-script> 正在工作,让我们编写一些 JavaScript!

我们要做的第一件事是获取我们将要使用的 DOM 元素并将它们存储在全局变量中。我们的代码将使用密码输入、提交按钮和显示密码规则的区域。将这三个声明添加到 validate.js

const passwordBox = document.getElementById("passwordBox");
const submitButton = document.getElementById("submitButton");
const rulesArea = document.getElementById("rules");

请注意,我们能够使用像 getElementById() 这样的常规 DOM API 方法。虽然我们的代码在 Worker 中运行,并且 Worker 缺乏对 DOM 的直接访问权限,但 <amp-script> 提供了 DOM 的虚拟副本并模拟了一些常见的 API,这些 API 在 此处 列出。这些 API 为我们提供了足够的工具来涵盖大多数用例。但重要的是要注意,仅支持 DOM API 的一个子集。否则,<amp-script> 中包含的 JavaScript 将非常庞大,从而抵消了 AMP 的性能优势!

我们需要将这些 id 添加到两个元素中。打开 index.html,找到密码 <input> 和提交 <button>,然后添加 id。还要向提交 <button> 添加一个 disabled 属性,以防止用户在我们需要他们点击之前点击它。

<input type=password
       id="passwordBox"

...

<button type="submit" id="submitButton" tabindex="3" disabled>Submit</button>

重新加载页面。您可以通过在控制台中进行检查来验证这些全局变量是否已正确设置,就像您使用非 Worker JavaScript 一样

我们还将为 <div id="rules"> 中的每个 <li> 添加 id。每个 <li> 都包含一个单独的规则,我们将需要控制其颜色。同时,我们还会删除每个 class="invalid" 的实例。我们的新 JavaScript 代码会在需要时添加它!

<ul>
  <li id="lower">Lowercase letter</li>
  <li id="upper">Capital letter</li>
  <li id="digit">Digit</li>
  <li id="special">Special character (@$!%*?&)</li>
  <li id="eight">At least 8 characters long</li>
</ul>

在 JavaScript 中实现我们的密码检查

接下来,我们将从 pattern 属性中解包正则表达式。每个正则表达式代表一个规则。让我们在 validate.js 的底部添加一个对象映射,将每个规则与其检查的标准关联起来。

const checkRegexes = {
  lower: /[a-z]/,
  upper: /[A-Z]/,
  digit: /\d/,
  special: /[^a-zA-Z\d]/i,
  eight: /.{8}/
};

设置好这些全局变量后,我们就可以编写检查密码并相应调整 UI 的逻辑了。我们将把逻辑放在一个名为 initCheckPassword 的函数中,该函数接收一个参数 - 密码 <input> 的 DOM 元素。这种方法方便地将 DOM 元素隐藏在闭包中。

function initCheckPassword(element) {

}

接下来,让我们用我们需要的功能和事件监听器赋值来填充 initCheckPassword。首先,添加一个小的函数,如果规则通过,则将单个规则 <li> 变为绿色;另一个函数则在规则失败时将其变为红色。

function initCheckPassword(el) {
  const checkPass = (el) => {
    el.classList.remove("invalid");
    el.classList.add("valid");
  };

  const checkFail = (el) => {
    el.classList.remove("valid");
    el.classList.add("invalid");
  };
};

让我们使 validinvalid 类真正地将文本变为绿色或红色。回到 index.html,并将这两个规则添加到 <style amp-custom> 标签中。

li.valid {
  color: #2d7b1f;
}

li.invalid {
  color:#c11136;
}

现在,我们准备添加逻辑,以根据我们的规则检查密码 <input> 的内容。在 initCheckPassword() 中,在右大括号之前,添加一个名为 checkPassword() 的新函数。

const checkPassword = () => {
  const password = element.value;
  let failed = false;

  for (const check in checkRegexes) {
    let li = document.getElementById(check);

    if (password.match(checkRegexes[check])) {
      checkPass(li);
    } else {
      checkFail(li);
      failed = true;
    }
  }

  if (!failed) {
    submitButton.removeAttribute("disabled");
  }
};

这个函数执行以下操作:

  1. 获取密码 <input> 的内容。
  2. 创建一个名为 failed 的标志,初始化为 false
  3. 遍历每个正则表达式,并针对密码测试每个正则表达式。
    • 如果密码测试失败,则调用 checkFail() 将相应的规则变为红色。同时,将 failed 设置为 true
    • 如果密码测试通过,则调用 checkPass() 将相应的规则变为绿色。
  4. 最后,如果没有规则失败,则密码有效,并且我们启用提交按钮。

现在我们只需要几个事件监听器。还记得我们无法在 AMP 中使用 focus 事件吗?在 <amp-script> 中,我们可以使用。每当密码 <input> 接收到 focus 事件时,我们将显示规则。并且,每当用户在该输入中按下键时,我们将调用 checkPassword()

initCheckPassword() 的底部,紧靠右大括号之前,添加这两个事件监听器。

element.addEventListener("focus", () => rulesArea.removeAttribute("hidden"));
element.addEventListener("keyup", checkPassword);

最后,在 validate.js 的末尾,添加一行代码,用密码 <input> 的 DOM 元素初始化 initCheckPassword

initCheckPassword(passwordBox);

我们的逻辑现在已经完成!当密码与我们所有的条件都匹配时,所有规则将变为绿色,并且我们的提交按钮将被启用。您现在应该能够进行类似这样的交互。

如果您遇到困难,可以随时参考 finished_code 目录中的工作代码。

恭喜你!

您已经学习了如何在 AMP 中使用 <amp-script> 编写自己的 JavaScript。您已经成功地使用自己的自定义逻辑和 UI 功能增强了 <amp-form> 组件!欢迎您向新页面添加更多功能!并且,要了解有关 <amp-script> 的更多信息,请查看参考文档