In some email implementations, the embedding iframe is sized according to "documentHeight" viewer messages sent from the AMP doc.
This "documentHeight" can be highly variable while loading some AMP emails e.g. when an email is basically one big amp-list.
For example, the Docs comment emails use an amp-list with layout="fixed-height". This results in the following "documentHeight" changes:
The latency between (1) and (2) results in a janky UX (caused by loading/execution of amp-list extension JS).
height value for layout=fixed-height. layout=container is what we want, but not allowed in amp-list.layout="container" would allow the amp-list to size itself per its child placeholder content. The issue is that it can cause content jumping after fetch+render.Â
So instead of disallowing layout="container" from the get-go, perhaps we can be more precise -- at time of rendering, check if the new contents would cause an illegal size change.Â
layout=container in amp-list.style="height: h, overflow: hidden". This temporarily makes the amp-list size static.style attribute.There's been recent effort to support more dynamic sizing in amp-list (e.g. changeToLayoutContainer action, [is-layout-container] binding) to avoid truncating children with dynamic sizes (e.g. amp-accordion/amp-ad). Also #25662.
This feature might remove the need for those and simplify amp-list's API by allowing layout=container from the start without a user gesture.
/cc @ampproject/wg-ui-and-a11y
@caroqliu Interested in driving this?
Happy to help, thanks for putting this on my radar!
Prototype: #26888
I'm confused. How does the proposal address this issue:
The latency between (1) and (2) results in a janky UX (caused by loading/execution of amp-list extension JS).
Fixing the height after JSON Fetch sounds like it only addresses the potential jump between (2) and (3).
That's right. We fix the height then because that's the only major risk of content jumping.
By allowing layout=container, we enable the amp-list to be sized to fit its placeholder on page load without any work.
Makes sense that the jump between (1) and (2) is unavoidable no matter the layout, but we can solve for the jump between (2) and (3).
So instead of disallowing layout="container" from the get-go, perhaps we can be more precise -- at time of rendering, check if the new contents would cause an illegal size change.
I'm still wrapping my head around the 9 different control branches for deciding if an element can be resized or not in resources-impl.js. Do you have any concern that this algorithm would leave some of the UX/behavior to chance?
One silly example of a case that would break expected amp-list behavior is if an email had content after the amp-list (which was meant to overflow off screen), but the user scrolls to the bottom of the email before the amp-list completes rendering. That would cause the it not to resize, potentially cutting off content.
header content
amp-list
---- intended fold, but user scrolls below it before the amp-list completes render -----
footer content
There is no jump between (1) and (2) with layout=container, because amp-list[height] is only needed for fixed-size layouts.
I think the edge case you describe was always an issue. E.g. desktop users with a huge viewport height might have the bottom of the amp-list in-viewport. Developers can use the overflow button to ask users to "expand to see all content".
For the edge case, I was under the impression that amp-list is always considered in the viewport because AMP emails are inside an iframe that always has the full height of the AMP document. Do browsers give away the parent viewport information?
Yea, the edge case is always true for Gmail and email providers that size the email iframe to the AMP page height.
Reopening because #27159 was reverted in #27554
When an amp-list has layout=container — but ends up getting its height fixed to avoid a jump — will it resize on the next user interaction that wouldn't cause a jump?
@chasefinch Not necessarily, though I can understand the use case. i.e. changeToLayoutContainer and [is-layout-container] are explicitly gated on user interaction so that any list resize and possible content jumping can be anticipated by the user.
In the case of an amp-list with layout=container, the list will follow the steps on render:
(1) lock height to the height of the placeholder or existing content
(2) render new contents and attempt resize
(3) unlock height only if content jumping would not occur
In this case if it is followed by a [is-layout-container] mutation or changeToLayoutContainer action (requiring user interaction), the list would then force unlock height even if content jumping would occur, under the same assumption that this is OK as before the layout=container feature was introduced.
@caroqliu gotcha. I assumed that was the case. I think I understand now that initial binding evaluation is the only thing that gets triggered on an arbitrary user interaction.
Thanks for clarifying, it helps to cover the expected behavior with existing features. One thing to note with the above interaction pattern is that while the UX is in line with the spirit of the current features (is-layout-container and changeToLayoutContainer, the names of the API used to achieve the behavior become misleading, as the list is not _changing layout format_ so much as it is forcing any height change required to fully display fresh content.
Yes, I see what you mean. Thanks!
So then:
layout=container never allows content jumping, even with user interaction.changeToLayoutContainer(): allows for content jumping, but only works a user interaction.Would it make sense to relax the layout=container restrictions further so that it allows for content jumping if there were a user gesture? If we did that, then it could fully replace the use-case for is-layout-container and changeToLayoutContainer
@samouri Yes that's right: The ability to force resize on user gesture is a reasonable addition to layout=container and could functionally replace the is-layout-container and changeToLayoutContainer features. Still, it likely would not lead to their deprecation in amp-list:0.1 due to their high usage today. In a future version of amp-list we could do away with these APIs entirely.
To this day, I still think there should be a separate rendering component (yes layout container, yes binding source, blah blah blah) and list component (and build infinite scroll on top of it w/o this container stuff)...
One point of subtlety though. I think the initial idea of guarding against content jumping was about the case where the user was looking at something below the list / load-more / whatever is loading, and then the content jump forcing the content that was holding the user's attention down, and creating a jarring visual experience.
Would it make sense to relax the layout=container restrictions further so that it allows for content jumping if there were a user gesture?
The concern here is if the load triggered by the user gesture is slow, the user could be interacting with things that later get pushed down would still be present. I don't know how much of a concern that is, but that's why there is a restriction somewhere in the runtime in "requestChangeSize" about denying the size change if there's X pixels or y% of the page height below the element requesting the size change. If layout=container relaxes that (which is-layout-container already did welp) you could have that as a consequence. Alternative ideas to consider is whether or not you want there to be worthwhile content beneath the loading thing, and whether you just want to render block below the list while loading.
@cathyxz I'd think that most size-changing user gestures wouldn't trigger network loads at all, but would be more like expanding elements, where the content is already loaded and the jump was instant. What kind of list-content interactions would cause a delayed load?
amp-bind can trigger a change in [src], right? It's actually not uncommon to use this (a button that triggers a src rebind) for server side filtering (e.g. ali express search). I think amp-list (currently) also has a reload feature that's either an action or a bindable attribute (don't remember specifics).
Ok, yeah, [src] is bindable. Is it possible to allow resize only for gestures that don't make a network request?
I think the challenge there is how to figure out if the action makes a network request. I'm not sure if the runtime categorises bind vs. actions that do or do not make a network request.
There's also [src] of elements inside amp-list (e.g. amp-img, amp-video, amp-ad) and the refresh action to consider: https://github.com/ampproject/amphtml/blob/master/extensions/amp-list/0.1/amp-list.js#L177.
But adding a way to categorize whether an action makes a network request is an interesting idea. =)
Most helpful comment
Happy to help, thanks for putting this on my radar!