Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use amp-elements for creating dynamic placeholders during buildCallback. #2846

Merged
merged 10 commits into from
May 6, 2016

Conversation

mkhatib
Copy link
Contributor

@mkhatib mkhatib commented Apr 7, 2016

Follow up to #2738 this is another approach we're thinking about how to get prerendering to work. This is a discussion PR to discuss the following approach,

This basically does what we kind of do today but instead of using native elements for placeholders the suggestion from @dvoytenko was to use amp-elements (i.e. amp-img in the examples below). This will allow the resources manager to manage loading these placeholders as well.

Few problems with this approach that I ran into:

  • amp-youtube hack for falling back to the hqdefault will no longer work unless we expose load promise for the amp-img or fire load event for it.
  • Initial tests seems to be troublesome with styling the amp-img directly to match what we want - Probably fixable by wrapping the amp-img with a container.

'left': '8px',
'right': '8px',
});
wrapper.appendChild(image);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attribute placeholder should be set somewhere here :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@dvoytenko
Copy link
Contributor

So, I definitely think this looks better, especially if we specifically focus on a separate createPlaceholderCallback. I'd even have it return the placeholder element as we discussed before and finish its styling from a common code. This gives us an option to add prerenderCallback in the future.

RE: YouTube
It's ok if we have an exception with them for now - I don't see a problem here. Loading two images is bad in either case. Ideally, let's reach out to them and see if we can improve that.

@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 11, 2016

Sounds good. I'll do some more refinement here and try to get this to work for most cases. For YouTube should we just use the sddefault (regardless if it'll fail or not) or stick to the sure lower quality hqdefault?

As for createPlaceholderCallback, I just want to make sure this makes sense and it's easy to understand when an element needs to override this, I am worried this implies that people need to override it just to have a placeholder.

But either case we can make this clear in our life-cycle docs that this is optionally gets called depending on whether we are able to detect a placeholder exists already at that time or not.

Let me do some more work on this to get a good visual result out of it and see if we can take it from there.

@dvoytenko
Copy link
Contributor

I'll do some more refinement here and try to get this to work for most cases. For YouTube should we just use the sddefault (regardless if it'll fail or not) or stick to the sure lower quality hqdefault?

I'm totally ok if we continue "the old way" with YouTube and much more interested in us reaching out to them to discuss and adjust. I think what we want is very straightforward and reasonable. (and possibly already exists).

As for createPlaceholderCallback, I just want to make sure this makes sense and it's easy to understand when an element needs to override this, I am worried this implies that people need to override it just to have a placeholder.

Let's think of a good name. But I think eventually it'll be the only way for us to reconcile this with "incomplete build" problem.

@cramforce
Copy link
Member

This is a really elegant solution. I think youtube is enough of a special case that we can keep it untouched for now. Getting this done without a new concept is great.

@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 14, 2016

Alright, I've updated the code with buildPlaceholderCallback as described in #2830 this also addresses the double placeholder problem mentioned in that bug. Let me know what do you folks think.

@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 14, 2016

One thing to notice is that I removed the YouTube thumbnails hack and now using hqdefault. The only ideas that I can think of about keeping the hack is as I mentioned before, expose amp-img internals namely img_ and updateImageSrc_, or allow amp-img to propagate the img load event maybe with an API like amp-img#onImageLoad.

@cramforce
Copy link
Member

Can we just leave youtube unchanged?

@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 14, 2016

Yeah we can leave the old hack in place and not implement the buildPlaceholderCallback.

@mkhatib mkhatib assigned cramforce and unassigned dvoytenko Apr 14, 2016
@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 14, 2016

Reverted amp-youtube change to keep the hack.

@mkhatib
Copy link
Contributor Author

mkhatib commented Apr 14, 2016

@cramforce let me know what do you think about this and if it looks good I can proceed of adding some tests.

/**
* Allow images inside amp-img to be fit-cover: object.
*/
amp-img.-amp-object-fit-cover > img {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Inside YouTube - I guess no longer since we reverted back to the hack instead of using amp-img I can drop this.

@cramforce
Copy link
Member

Looks great

.-amp-element > [placeholder] {
display: none;
}
.-amp-element > [placeholder]:last-of-type {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this doesn't really mean what I thought it meant. This only checks the tag so this wouldn't work as expected unless the placeholders are of the same tag type.

This breaks things, multiple placeholders will still show if they're of different types because the selector will match them all. I am trying to think of a way around this but so far coming empty. Let me know if you have ideas on how to deal with this.

cc/ @cramforce @erwinmombay @dvoytenko - see for more details #2830

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a way to do this but for the first match using ~ selector
See: http://stackoverflow.com/a/8539107/646979

However, I doubt this can work somehow for last match. So one way to work this out is for us to adapt the first placeholder instead of the last. The problem with this is that in case the double placeholder problem happens, our CSS would choose the dynamic placeholder instead of the user provided one which doesn't really solve the whole problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok after tons of experiments it seems it is impossible to do last-match in CSS as we want here. I came up with a different idea of how to approach this. Still in CSS.

Instead of hiding placeholders we just show all placeholders with trbl=0 as it's currently is. The only difference is that we set a background for the placeholders (e.g. #efefef), this will force the last placeholder to show at the top and virtually hide all the other placeholders.

The other change that we'd need to do is with togglePlaceholder which now needs to toggle ALL placeholders instead of just the one. I don't see any problem with this.

I'll update the code to reflect this but if there are any concerns with this please let me know.

@mkhatib mkhatib changed the title WIP: Use amp-elements for creating dynamic placeholders during buildCallback. Use amp-elements for creating dynamic placeholders during buildCallback. Apr 18, 2016
* Finds all child elements that satisfies the callback.
* @param {!Element} parent
* @param {function(!Element):boolean} callback
* @return {Array.<Element>}
Copy link
Member

@cramforce cramforce Apr 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!Array<!Element>

@cramforce
Copy link
Member

I think we should wait for a final review by @dvoytenko

@cramforce cramforce assigned dvoytenko and unassigned cramforce Apr 19, 2016
@@ -148,6 +148,8 @@ i-amp-sizer {

.-amp-element > [placeholder] {
display: block;
/** Background is important to hide all other placeholders below it. */
background-color: #efefef;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not too big of a change? All existing placeholders will now get the differing background?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can stick with a white background and maybe make this somewhat less specific so it would allow other selectors to override this correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped BG.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

setStyles(this.placeholderWrapper_, {
'display': '',
});
this.togglePlaceholder(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to go into the runtime level. Whoever calls unlayoutCallback should also toggle placeholder back on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Moved inside Resources.unlayout

@dvoytenko
Copy link
Contributor

@mkhatib Couple more comments, otherwise looks very good.

@mkhatib
Copy link
Contributor Author

mkhatib commented May 4, 2016

Thanks @dvoytenko moved placeholder toggling to resources. PTAL

@dvoytenko
Copy link
Contributor

LGTM! This is a great improvement. But let's merge tomorrow if you don't mind. I'd like to squeeze in the isReadyToBuild improvement into the same release with this fix.

return this.implementation_.unlayoutCallback();
const isReLayoutNeeded = this.implementation_.unlayoutCallback();
if (isReLayoutNeeded) {
this.layoutCount_ = 0;
Copy link
Contributor

@jridgewell jridgewell May 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this before... In resources-impl.js, never mind.

@jridgewell
Copy link
Contributor

How do we get into a situation where there are multiple placeholders?

@mkhatib
Copy link
Contributor Author

mkhatib commented May 5, 2016

@jridgewell this might happen for elements that create dynamic placeholders (youtube and instagram as examples), the reason is that the element would start being built by AMP before all of its tree is ready, so AMP would think there's no placeholder for this element because the placeholder has not actually been parsed by the browser yet.

<amp-youtube>
  <div placeholder></div> <-- if build starts before this was parsed amp-youtube will dynamically create another placeholder and insert it - leaving us with two placeholders. 
</amp-youtube>

@jridgewell
Copy link
Contributor

Thanks! This may be impossible to test, but which placeholder is last in that case? We use #appendChild on a childless element, so are new parsed children appended after our created placeholder? (I'm asking because I think we definitely want to prioritize the user declared, not-yet-parsed placeholder over our own).

@dvoytenko
Copy link
Contributor

@mkhatib @jridgewell - this would be a very rare problem. We should avoid the whole problem altogether. Let's try to fix #3081 by next canary.

@dvoytenko
Copy link
Contributor

@mkhatib @jridgewell also important to note - this is the problem we already have today, so we are not making it worse - just trying to make it better.

@mkhatib
Copy link
Contributor Author

mkhatib commented May 5, 2016

@jridgewell yes I think the user provided one would be appended later that's why we take the last placeholder to be the element placeholder. I tested this with something like this, though I am not entirely sure if this really emulates the same situation.

<element>
  <script>element.appendChild(dynamic-placeholder)</script>
  <user-placeholder></user-placeholder>
</element>

This would create the following DOM:

<element>
  <dynamic-placeholder>
  <user-placeholder>
</element>

@mkhatib mkhatib merged commit 074343b into ampproject:master May 6, 2016
@mkhatib mkhatib deleted the prerender-during-build branch May 6, 2016 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants