-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
797 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<!--- | ||
Copyright 2016 The AMP HTML Authors. All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS-IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
|
||
# <a name="amp-sticky-ad"></a> `amp-sticky-ad` | ||
|
||
<table> | ||
<tr> | ||
<td width="40%"><strong>Description</strong></td> | ||
<td>A stickyAd provides a way to fix ad at bottom of a page. The stickyAs serves as a container and the ad as its child will display as sticky-ad</td> | ||
</tr> | ||
<tr> | ||
<td width="40%"><strong>Availability</strong></td> | ||
<td><div><a href="https://www.ampproject.org/docs/reference/experimental.html">Experimental</a>; no validations yet.</div><div>Work in progress.</div></td> | ||
</tr> | ||
<tr> | ||
<td width="40%"><strong>Required Script</strong></td> | ||
<td><code><script async custom-element="amp-sticky-ad" src="https://cdn.ampproject.org/v0/amp-sticky-ad-0.1.js"></script></code></td> | ||
</tr> | ||
<tr> | ||
<td class="col-fourty"><strong><a href="https://www.ampproject.org/docs/guides/responsive/control_layout.html">Supported Layouts</a></strong></td> | ||
<td>NODISPLAY</td> | ||
</tr> | ||
</table> | ||
|
||
## Behavior | ||
|
||
- There can be only one `<amp-sticky-ad>` in an AMP document. The `<amp-sticky-ad>` should only have one direct child of `<amp-ad>`. | ||
- The sticky will appear on the bottom of a page. | ||
- The height of the sticky-ad is whatever its child needs up to its max-height. | ||
- The max-height of the sticky-ad is 100px, if the height exceeds 100px then the height would be 100px and overflow content will be hidden. | ||
- The width of the sticky-ad is set to 100% using CSS and cannot be overridden. | ||
- The sticky-ad will display after scroll one viewport height from top provided there is at least one more viewport of content available. | ||
- Swipe to dismiss (TBD) | ||
|
||
Example: | ||
```html | ||
<amp-sticky-ad layout="nodisplay"> | ||
<amp-ad width="300" height="65" | ||
type="adman" | ||
data-ws="17342" | ||
data-s="300x65" | ||
data-host="talos.adman.gr"> | ||
</amp-ad> | ||
</amp-sticky-ad> | ||
``` | ||
|
||
## Attributes | ||
|
||
**layout** | ||
|
||
The only permissible value for the `layout` attribute in `amp-sticky-ad` is `nodisplay`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* Copyright 2016 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
amp-sticky-ad { | ||
position: fixed !important; | ||
text-align:center; | ||
bottom: 0 !important; | ||
left: 0; | ||
width: 100% !important; | ||
z-index: 11; | ||
max-height: 100px !important; | ||
box-sizing: border-box; | ||
} | ||
|
||
.i-amphtml-sticky-ad-layout { | ||
display: flex; | ||
visibility: hidden !important; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
overflow: visible !important; | ||
transform: translateZ(0) !important; | ||
} | ||
|
||
amp-sticky-ad[visible] { | ||
visibility: visible !important; | ||
} | ||
|
||
.amp-sticky-ad-loaded { | ||
background-color: #fff; | ||
} | ||
|
||
.i-amphtml-sticky-ad-layout > amp-ad { | ||
display: block; | ||
} | ||
|
||
|
||
.amp-sticky-ad-close-button { | ||
position: absolute; | ||
visibility: hidden; | ||
width: 32px; | ||
height: 32px; | ||
top: -32px; | ||
right: 0; | ||
background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'); | ||
background-size: 26px 26px; | ||
background-position: center; | ||
background-color: #fff; | ||
border: 1px solid #c8c8c8; | ||
} | ||
|
||
amp-sticky-ad[visible] > .amp-sticky-ad-close-button { | ||
visibility: visible; | ||
} | ||
|
||
/* Increase tapping area of the dismiss button */ | ||
.amp-sticky-ad-close-button:before { | ||
position: absolute; | ||
content: ''; | ||
top: -18px; | ||
right: 0; | ||
left: -18px; | ||
bottom: 0; | ||
} | ||
|
||
[dir=rtl] .amp-sticky-ad-close-button { | ||
right: auto; | ||
left: 0; | ||
} | ||
|
||
[dir=rtl] .amp-sticky-ad-close-button:before { | ||
top: -18px; | ||
right: -18px; | ||
left: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/** | ||
* Copyright 2016 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {CommonSignals} from '../../../src/common-signals'; | ||
import {CSS} from '../../../build/amp-sticky-ad-0.1.css'; | ||
import {Layout} from '../../../src/layout'; | ||
import {dev,user} from '../../../src/log'; | ||
import {removeElement} from '../../../src/dom'; | ||
import {toggle} from '../../../src/style'; | ||
|
||
class AmpStickyAd extends AMP.BaseElement { | ||
/** @param {!AmpElement} element */ | ||
constructor(element) { | ||
super(element); | ||
|
||
/** @const @private {!../../../src/service/vsync-impl.Vsync} */ | ||
this.vsync_ = this.getVsync(); | ||
|
||
/** @private {?Element} */ | ||
this.ad_ = null; | ||
|
||
/** @private {?../../../src/service/viewport-impl.Viewport} */ | ||
this.viewport_ = null; | ||
|
||
/** @private {boolean} */ | ||
this.visible_ = false; | ||
|
||
/** @private {?UnlistenDef} */ | ||
this.scrollUnlisten_ = null; | ||
} | ||
|
||
/** @override */ | ||
isLayoutSupported(layout) { | ||
return layout == Layout.NODISPLAY; | ||
} | ||
|
||
/** @override */ | ||
buildCallback() { | ||
this.viewport_ = this.getViewport(); | ||
|
||
toggle(this.element, true); | ||
this.element.classList.add('i-amphtml-sticky-ad-layout'); | ||
const children = this.getRealChildren(); | ||
user().assert((children.length == 1 && children[0].tagName == 'AMP-AD'), | ||
'amp-sticky-ad must have a single amp-ad child'); | ||
|
||
this.ad_ = children[0]; | ||
this.setAsOwner(this.ad_); | ||
|
||
// On viewport scroll, check requirements for amp-stick-ad to display. | ||
this.scrollUnlisten_ = | ||
this.viewport_.onScroll(() => this.displayAfterScroll_()); | ||
} | ||
|
||
/** @override */ | ||
layoutCallback() { | ||
// Reschedule layout for ad if layout sticky-ad again. | ||
if (this.visible_) { | ||
toggle(this.element, true); | ||
const borderBottom = this.element./*OK*/offsetHeight; | ||
this.viewport_.updatePaddingBottom(borderBottom); | ||
this.updateInViewport(dev().assertElement(this.ad_), true); | ||
this.scheduleLayout(dev().assertElement(this.ad_)); | ||
} | ||
return Promise.resolve(); | ||
} | ||
|
||
/** @override */ | ||
unlayoutCallback() { | ||
this.viewport_.updatePaddingBottom(0); | ||
this.element.classList.remove('amp-sticky-ad-loaded'); | ||
return true; | ||
} | ||
|
||
/** @override */ | ||
detachedCallback() { | ||
this.removeOnScrollListener_(); | ||
} | ||
|
||
/** @override */ | ||
collapsedCallback() { | ||
toggle(this.element, false); | ||
this.vsync_.mutate(() => { | ||
this.viewport_.updatePaddingBottom(0); | ||
}); | ||
} | ||
|
||
/** | ||
* The function that remove listener to viewport onScroll event. | ||
* @private | ||
*/ | ||
removeOnScrollListener_() { | ||
if (this.scrollUnlisten_) { | ||
this.scrollUnlisten_(); | ||
this.scrollUnlisten_ = null; | ||
} | ||
} | ||
|
||
/** | ||
* The listener function that listen on onScroll event and | ||
* show sticky ad when user scroll at least one viewport and | ||
* there is at least one more viewport available. | ||
* @private | ||
*/ | ||
displayAfterScroll_() { | ||
const scrollTop = this.viewport_.getScrollTop(); | ||
const viewportHeight = this.viewport_.getSize().height; | ||
const scrollHeight = this.viewport_.getScrollHeight(); | ||
if (scrollHeight < viewportHeight * 2) { | ||
this.removeOnScrollListener_(); | ||
return; | ||
} | ||
|
||
// Check user has scrolled at least one viewport from init position. | ||
if (scrollTop > viewportHeight) { | ||
this.removeOnScrollListener_(); | ||
this.deferMutate(() => { | ||
this.visible_ = true; | ||
this.viewport_.addToFixedLayer(this.element); | ||
// Add border-bottom to the body to compensate space that was taken | ||
// by sticky ad, so no content would be blocked by sticky ad unit. | ||
const borderBottom = this.element./*OK*/offsetHeight; | ||
this.viewport_.updatePaddingBottom(borderBottom); | ||
this.addCloseButton_(); | ||
this.scheduleLayoutForAd_(); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Function that check if ad has been built. If not, wait for the "built" | ||
* signal. Otherwise schedule layout for ad. | ||
* @private | ||
*/ | ||
scheduleLayoutForAd_() { | ||
if (this.ad_.isBuilt()) { | ||
this.layoutAd_(); | ||
} else { | ||
this.ad_.whenBuilt().then(this.layoutAd_.bind(this)); | ||
} | ||
} | ||
|
||
/** | ||
* Layout ad, and display sticky-ad container after layout complete. | ||
* @private | ||
*/ | ||
layoutAd_() { | ||
const ad = dev().assertElement(this.ad_); | ||
this.updateInViewport(ad, true); | ||
this.scheduleLayout(ad); | ||
// Wait for the earliest: `render-start` or `load-end` signals. | ||
// `render-start` is expected to arrive first, but it's not emitted by | ||
// all types of ads. | ||
const signals = ad.signals(); | ||
return Promise.race([ | ||
signals.whenSignal(CommonSignals.RENDER_START), | ||
signals.whenSignal(CommonSignals.LOAD_END), | ||
]).then(() => { | ||
return this.vsync_.mutatePromise(() => { | ||
// Set sticky-ad to visible and change container style | ||
this.element.setAttribute('visible', ''); | ||
this.element.classList.add('amp-sticky-ad-loaded'); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* The function that add a close button to sticky ad | ||
* @private | ||
*/ | ||
addCloseButton_() { | ||
const closeButton = this.win.document.createElement('button'); | ||
closeButton.classList.add('amp-sticky-ad-close-button'); | ||
closeButton.setAttribute('aria-label', | ||
this.element.getAttribute('data-close-button-aria-label') | ||
|| 'Close this ad'); | ||
const boundOnCloseButtonClick = this.onCloseButtonClick_.bind(this); | ||
closeButton.addEventListener('click', boundOnCloseButtonClick); | ||
this.element.appendChild(closeButton); | ||
} | ||
|
||
/** | ||
* The listener function that listen to click event and dismiss sticky ad | ||
* @private | ||
*/ | ||
onCloseButtonClick_() { | ||
this.vsync_.mutate(() => { | ||
this.visible_ = false; | ||
this./*OK*/scheduleUnlayout(dev().assertElement(this.ad_)); | ||
this.viewport_.removeFromFixedLayer(this.element); | ||
removeElement(this.element); | ||
this.viewport_.updatePaddingBottom(0); | ||
}); | ||
} | ||
} | ||
|
||
AMP.extension('amp-sticky-ad', '0.1', AMP => { | ||
AMP.registerElement('amp-sticky-ad', AmpStickyAd, CSS); | ||
}); |
Oops, something went wrong.