diff --git a/examples/bind/title.amp.html b/examples/bind/title.amp.html new file mode 100644 index 000000000000..b8f31dfb5751 --- /dev/null +++ b/examples/bind/title.amp.html @@ -0,0 +1,35 @@ + + + + + amp-bind: Title + + + + + + + + + + + +

Title changing example

+

After clicking the button below, a counter will be appended to this page's title in the browser and the number will increment on each subsequent button push.

+

+ +
+ + + + + + diff --git a/extensions/amp-bind/0.1/bind-impl.js b/extensions/amp-bind/0.1/bind-impl.js index d9567df6492a..c87cf31dcaba 100644 --- a/extensions/amp-bind/0.1/bind-impl.js +++ b/extensions/amp-bind/0.1/bind-impl.js @@ -23,13 +23,13 @@ import {ChunkPriority, chunk} from '../../../src/chunk'; import {Services} from '../../../src/services'; import {deepMerge, dict} from '../../../src/utils/object'; import {dev, user} from '../../../src/log'; +import {elementByTag, iterateCursor, waitForBodyPromise} from '../../../src/dom'; import {filterSplice} from '../../../src/utils/array'; import {getMode} from '../../../src/mode'; import {installServiceInEmbedScope} from '../../../src/service'; import {invokeWebWorker} from '../../../src/web-worker/amp-worker'; import {isArray, isObject, toArray} from '../../../src/types'; import {isFiniteNumber} from '../../../src/types'; -import {iterateCursor, waitForBodyPromise} from '../../../src/dom'; import {map} from '../../../src/utils/object'; import {parseJson, recursiveEquals} from '../../../src/json'; import {reportError} from '../../../src/error'; @@ -161,7 +161,8 @@ export class Bind { */ this.initializePromise_ = this.viewer_.whenFirstVisible().then(() => bodyPromise).then(body => { - return this.initialize_(body); + return this.initialize_( + body, elementByTag(ampdoc.getHeadNode(), 'title')); }); /** @private {Promise} */ @@ -282,14 +283,19 @@ export class Bind { /** * Scans the ampdoc for bindings and creates the expression evaluator. * @param {!Node} rootNode + * @param {Node} titleNode * @return {!Promise} * @private */ - initialize_(rootNode) { + initialize_(rootNode, titleNode) { dev().fine(TAG, 'Scanning DOM for bindings and macros...'); + const nodes = [rootNode]; + if (titleNode) { + nodes.push(titleNode); + } let promise = Promise.all([ this.addMacros_(), - this.addBindingsForNodes_([rootNode])] + this.addBindingsForNodes_(nodes)] ).then(() => { // Listen for DOM updates (e.g. template render) to rescan for bindings. rootNode.addEventListener(AmpEvents.DOM_UPDATE, this.boundOnDomUpdate_); @@ -868,6 +874,9 @@ export class Bind { switch (property) { case 'text': element.textContent = String(newValue); + if (tag === 'TITLE') { + this.localWin_.document.title = String(newValue); + } // Setting `textContent` on TEXTAREA element only works if user // has not interacted with the element, therefore `value` also needs // to be set (but `value` is not an attribute on TEXTAREA) diff --git a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js index 5f8266e45048..02de5773a295 100644 --- a/extensions/amp-bind/0.1/test/integration/test-bind-impl.js +++ b/extensions/amp-bind/0.1/test/integration/test-bind-impl.js @@ -368,6 +368,16 @@ describe.configure().ifNewChrome().run('Bind', function() { }); }); + it('should update document title for elements', () => { + const element = createElement( + env, container, '[text]="\'a\' + \'b\' + \'c\'"', 'title'); + element.value = 'foo'; + return onBindReadyAndSetState(env, bind, {}).then(() => { + expect(element.value).to.equal('abc'); + expect(env.win.document.title).to.equal('abc'); + }); + }); + it('should support binding to CSS classes with strings', () => { const element = createElement(env, container, '[class]="[\'abc\']"'); expect(toArray(element.classList)).to.deep.equal([]); diff --git a/validator/validator-main.protoascii b/validator/validator-main.protoascii index b022e73851f2..ce34abe4df2a 100644 --- a/validator/validator-main.protoascii +++ b/validator/validator-main.protoascii @@ -120,6 +120,7 @@ tags: { tags: { tag_name: "TITLE" spec_name: "title" + attrs: { name: "[text]" } } # 4.2.3 the base element tags: {