amp-script
簡介
amp-script
元件允許您執行自訂 JavaScript。您的程式碼會在 Web Worker 中執行,並適用某些限制。
設定
首先,您需要匯入 amp-script
擴充功能。
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
對於內嵌腳本,您需要產生腳本雜湊值。在開發期間,使用 data-ampdevmode
屬性停用此要求。請造訪文件以瞭解更多資訊。
<meta name="amp-script-src" content="sha384-iER2Cy-P1498h1B-1f3ngpVEa9NG1xIxKqg0rNkRX0e7p5s0GYdit1MRKsELIQe8 sha384-UPY0FmlOzIjSqWqMgbuaEbqIdvpGY_FzCuTAyoLdrFJb2NYf8cPWJlugA0rUbXjL
從 URL 載入腳本
若要從 URL 載入您的腳本,請使用 src
屬性。此範例載入並執行名為 hello.js
的腳本。有效的 AMP 要求所有 URL 皆為絕對路徑並使用 https
。
以下是 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); });
以及 HTML 如下
<amp-script layout="container" src="https://amp.dev.org.tw/documentation/examples/components/amp-script/hello-world.js" class="sample">
<button id="hello-url">Say hello!</button>
</amp-script>
使用內嵌腳本
您也可以內嵌腳本,並透過 id
參考它。請注意,在腳本中,您需要設定 type=text/plain
和 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>
amp-script
將其子元素作為虛擬 DOM 傳遞給您的腳本 - 而非整個 DOM。對您的腳本而言,這些子元素*就是* DOM。因此,document.body
指的是 amp-script
標籤內的內容,而非實際的 body
。document.body.appendChild(...)
實際上是在 amp-script
元素內部新增一個元素。使用 fetch API
amp-script
支援 fetch API。如果 amp-script
知道腳本無法變更元件的高度,則允許我們在載入時更新頁面。在這裡,我們使用 fixed-height
版面配置,並在 HTML 屬性中指定 height
。請參閱文件以瞭解詳細資訊。
<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.org.tw/documentation/examples/api/time');
const data = await response.json();
const span = document.getElementById('time');
span.textContent = data.time;
}
fetchCurrentTime();
</script>
多次 fetch
在大小可變更的容器中,您的程式碼可以在最後一次 fetch()
完成後的 5 秒內進行 DOM 變更。此範例多次呼叫一個速度較慢的 API。它會在每次呼叫傳回時顯示結果。
<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.org.tw/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>
amp-list
的資料來源
<amp-script>
函式可以作為 <amp-list>
的資料來源。
-
使用
exportFunction()
使函式對<amp-list>
可見。 -
在
<amp-list>
的src
屬性中指定腳本和函式。使用src="amp-script:{scriptID}:functionName"
格式,其中{scriptID}
是<amp-script>
的id
,而{functionName}
是匯出函式的名稱。 -
您可以在
<amp-script>
中使用nodom
屬性來指示<amp-script>
不需要 DOM。這可以提高效能,因為amp-script
不需要載入或執行其虛擬 DOM 實作。
<div class="sample">
<amp-script id="dataFunctions" script="amp-list-source-script" nodom></amp-script>
<script id="amp-list-source-script" type="text/plain" target="amp-script">
function fetchData() {
return fetch('https://amp.dev.org.tw/static/samples/json/todo.json')
.then(response => response.json())
.then(transformData);
}
function transformData(json) {
let newEntries =
json.items.map(
entry => (entry.done ? 'Already done: ' : 'To do: ') + entry.title
);
return { items: newEntries };
}
exportFunction('fetchData', fetchData);
</script>
<amp-list width="auto" height="70" layout="fixed-height" src="amp-script:dataFunctions.fetchData">
<template type="amp-mustache">
<div>{{.}}</div>
</template>
</amp-list>
</div>
使用 WebSocket 進行即時更新
amp-script
支援 WebSockets。此範例模擬即時部落格。
<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.org.tw/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>
顯示即時資料
您也可以使用 setInterval()
或 setTimeout
來取得最新資料。
<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.org.tw/documentation/examples/api/time');
const data = await response.json();
span.textContent = data.time;
}
setInterval(fetchCurrentTime, 1000);
</script>
自訂表單驗證
您也可以使用 amp-script
來實作自訂表單驗證。當輸入欄位僅包含大寫字母時,此腳本會啟用按鈕。
<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>
偵測作業系統
您的腳本可以存取全域物件,例如 navigator
。此腳本使用此物件來嘗試猜測您裝置的作業系統。
<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>
個人化
同樣地,您可以使用 navigator
物件或其他方式,為您的使用者個人化內容。以下腳本偵測瀏覽器的語言並顯示本地化問候語。
<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);
let translation = translationMap[lang];
if (!translation) {
translation = "Couldn't recognize your language. So: Saluton";
}
let greeting = document.getElementById('translated-greeting');
greeting.innerHTML = translation + '!';
</script>
與 <amp-state>
互動
您的腳本可以使用狀態變數和繫結來影響 <amp-script>
元件外部的區域。在這裡,當按鈕被點擊時,我們會將狀態變數的值設定為圖片 URL。該狀態變數繫結至 <amp-img>
的 src
屬性。
<amp-state id="imgSrc">
<script type="application/json">
"product1_640x426.jpg"
</script>
</amp-state>
<amp-img layout="responsive" height="426" width="640" src="https://amp.dev.org.tw/static/samples/img/product1_640x426.jpg" [src]="'https://amp.dev.org.tw/static/samples/img/' + 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');
appleButton.addEventListener(
'click',
() => AMP.setState({imgSrc: 'product1_640x426.jpg'})
);
orangeButton.addEventListener(
'click',
() => AMP.setState({imgSrc: 'product2_640x426.jpg'})
);
</script>
與 AMP 元件互動
您的腳本可以使用狀態變數和繫結來與 AMP 元件通訊。將元件中的屬性繫結至包含狀態變數的運算式。當您的腳本修改該狀態變數時,變更將會傳播到元件。同樣地,如果 AMP 元件變更狀態變數的值,您的腳本可以取得新值。此腳本驅動一個按鈕,該按鈕會以隨機方向發送圖片輪播。
幻燈片 1 / 3
<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.org.tw/static/inline-examples/images/image1.jpg" layout="responsive" width="450" height="300"></amp-img>
<amp-img src="https://amp.dev.org.tw/static/inline-examples/images/image2.jpg" layout="responsive" width="450" height="300"></amp-img>
<amp-img src="https://amp.dev.org.tw/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">
Random direction
</button>
</amp-script>
<script id="carousel-script" type="text/plain" target="amp-script">
const button = document.getElementById('carousel-button');
async function randomSlideDirection() {
let oldSlide = Number(await AMP.getState('slideIndex'));
let addend = Math.ceil(Math.random() * 2);
let newSlide = (oldSlide + addend) % 3;
AMP.setState({slideIndex: newSlide});
}
button.addEventListener('click', randomSlideDirection);
</script>
</div>
本機儲存空間
您的腳本可以存取 Web Storage API。這讓您可以跨瀏覽器工作階段保存使用者資訊。在這裡,我們使用 localStorage
來追蹤使用者名稱。如果您設定使用者名稱,然後重新載入此頁面,則使用者名稱將保持設定。
<amp-script layout="fixed-height" script="local-storage-script" height="110" class="sample">
<div>
<span>Current username: </span>
<b id="username-display"></b>
</div>
<div>
<label>Enter new username:</label>
<input id="username-input" type="text" maxlength="20">
</div>
<button id="username-submit">Submit</button>
</amp-script>
<script id="local-storage-script" type="text/plain" target="amp-script">
const submit = document.getElementById('username-submit');
const input = document.getElementById('username-input');
const display = document.getElementById('username-display');
const oldUsername = localStorage.getItem('username');
display.textContent = oldUsername ? oldUsername : '(not set)';
function setUsername() {
const newUsername = input.value;
localStorage.setItem('username', newUsername);
display.textContent = newUsername;
}
submit.addEventListener('click', setUsername);
</script>
如果此頁面上的說明未涵蓋您的所有問題,請隨時與其他 AMP 使用者聯繫,討論您的確切使用案例。
前往 Stack Overflow 未說明的特色?AMP 專案強烈鼓勵您的參與和貢獻!我們希望您能成為我們開放原始碼社群的持續參與者,但我們也歡迎針對您特別熱衷的問題做出一次性貢獻。
在 GitHub 上編輯範例