-
Notifications
You must be signed in to change notification settings - Fork 140
Description
Originally discussed in #1919 (comment) and in other comments on that PR.
The OD_HTML_Tag_Processor class includes two methods: ::append_head_html() and ::append_body_html(). The OD_HTML_Tag_Processor instance is exposed on an OD_Tag_Visitor_Context instance passed to the tag visitors, but it is not yet exposed on the OD_Template_Optimization_Context class which is passed to the od_start_template_optimization and od_finish_template_optimization actions which run before and after the document has been iterated over by tag visitors, respectively.
The current use cases for ::append_head_html() are:
- Optimization Detective injecting the
LINKHTML markup returned byOD_Link_Collection::get_html(). - Image Prioritizer adding a
STYLEtag via a tag visitor to add a style for lazy loaded background images. - Embed Optimizer adding a
STYLEtag via a tag visitor to reduce layout shifts. - Content Visibility adding a
STYLEtag via a tag visitor for CV styles.
The current use cases for ::append_body_html() are:
- Optimization Detective uses this to insert the
detect.jsscript to the page. - Embed Optimizer adding a
SCRIPTto the end of theBODYwhen there is a lazy-loaded embed on the page. - Image Prioritizer adding a
SCRIPTtag via a tag visitor to lazy load background images. - Image Prioritizer adding a
SCRIPTtag via a tag visitor to lazy load videos.
Allowing insertion of HTML once via the od_finish_template_optimization avoids the need for tag visitors to keep track of whether they inserted or not. They can use the tag visitor callbacks to get a "lay of the land" by looking at all of the tags, and then at the od_finish_template_optimization action they can finalize what they need to insert in the HEAD or the BODY.
This could, for example, allow tag visitors to better optimize stylesheets they insert into the document. Instead of Embed Optimizer inserting a separate STYLE for each embed to reserve space to reduce layout shifts, it could instead insert a single STYLE at od_finish_template_optimization which combines all the style rules in one stylesheet. For example, this would allow Embed Optimizer to better group styles by media query instead of having to output @media (width <= 480px) {} for each embed on the page. Currently for each embed it inserts a STYLE like:
<style>
@media (width <= 480px) { #embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; } }
@media (480px < width <= 600px) { #embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; } }
@media (600px < width <= 782px) { #embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; } }
@media (782px < width) { #embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; } }
</style>
<style>
@media (width <= 480px) { #embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; } }
@media (480px < width <= 600px) { #embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; } }
@media (600px < width <= 782px) { #embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; } }
@media (782px < width) { #embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; } }
</style>
<style>
@media (width <= 480px) { #embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; } }
@media (480px < width <= 600px) { #embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; } }
@media (600px < width <= 782px) { #embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; } }
@media (782px < width) { #embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; } }
</style>With the od_finish_template_optimization action, it could just print the @media at-rules once for each viewport group rather than duplicating them, and output them all in a single STYLE tag rather than in three separate ones, for example:
<style>
@media (width <= 480px) {
#embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; }
#embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; }
#embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; }
}
@media (480px < width <= 600px) {
#embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; }
#embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; }
#embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; }
}
@media (600px < width <= 782px) {
#embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; }
#embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; }
#embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; }
}
@media (782px < width) {
#embed-optimizer-6040306707fb51ccaafa915c2da8f412 { min-height: 500px; }
#embed-optimizer-96ffd32b51748c70b288af1ef0c14c01 { min-height: 500px; }
#embed-optimizer-f9ff6c9a914366ac3c1aab1994dd8a69 { min-height: 500px; }
}
</style>See #1923 for a proof of concept for this.
Additionally, for tag visitors that insert scripts at the end of the BODY based on whether certain tags are encountered, this could be done via the od_finish_template_optimization action instead. Otherwise the tag visitor needs to keep track with a added_lazy_script class member variable for whether it inserted the script yet, although the difference here is not as great as with the stylesheets in Embed Optimizer. Example: #1922.
Being able to insert HTML via these actions would also be useful to a plugin like Optimization Detective Admin UI which needs to insert the current response's URL Metric data for rendering in the admin bar. See westonruter/od-admin-ui#11 for an example of how this could be implemented.
Currently when tag visitors call ::append_head_html() or ::append_body_html(), they must be sure to pass raw HTML which is valid in the HEAD and BODY context respectively. If they don't, then they'll cause a parsing error for the browser (e.g. adding an IMG in the HEAD will prematurely open the BODY).
Optimization Detective could continue to use the ::append_head_html() and ::append_body_html(), but for extensions there would need to be new methods like:
::append_head_style()to append an inline stylesheet::append_head_script()(but also differentiate between inline vs non-inline?)::append_body_script()(ditto)
We wouldn't need to include an ::append_head_link() since this is what the OD_Link_Collection takes care of, but we may need to add support for rel=stylesheet.
Other potential methods that will be useful:
::append_body_stylesheet_link()(not used yet, but useful for adding non-blocking external stylesheets)::append_head_meta()to add aMETAtag::append_body_comment()to add a comment to the end of theBODY::append_document_comment()to add a comment after the closing</html>tag.
Note that we should perhaps not allow script modules to be inserted in the HEAD because import map scripts (SCRIPT[type="importmap"]) are printed in the footer in Classic Themes, and if a script module appears before an import map then it prevents the import map from working.
If these methods were exposed on both OD_Tag_Visitor_Context and OD_Template_Optimization_Context then the ::append_html_html() and ::append_body_html() methods could be deprecated and eventually not exposed at all.
See also the following from #1546:
We should consider not directly exposing the
OD_HTML_Tag_Processorto the tag visitors and template optimization start/finish action handlers since this then exposes theappend_head_html()andappend_body_html()methods which may not be desirable. This also exposes a lower-level API than which may be appropriate which will complicate switching between usingWP_HTML_Tag_ProcessorandWP_HTML_Processorimplementations. Namely, forWP_HTML_Processorthere may not be a need to add aget_xpath()method directly to the class, and we may not need to subclass it at all. If we instead passed a wrapper class, then we could expose aget_xpath()on the wrapper subclass which is then able to use the appropriate implementation based on whether it is usingWP_HTML_Tag_ProcessororWP_HTML_Processor. In fact, this could allow Optimization Detective to use an unsubclassed originalWP_HTML_Tag_Processorinstance which is provided by core for output-buffer manipulation instead of allowing filtering the underlying string (if that is so desired, per Core-43258).
Metadata
Metadata
Assignees
Labels
Type
Projects
Status