-
Notifications
You must be signed in to change notification settings - Fork 128
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
LINK
HTML markup returned byOD_Link_Collection::get_html()
. - Image Prioritizer adding a
STYLE
tag via a tag visitor to add a style for lazy loaded background images. - Embed Optimizer adding a
STYLE
tag via a tag visitor to reduce layout shifts. - Content Visibility adding a
STYLE
tag via a tag visitor for CV styles.
The current use cases for ::append_body_html()
are:
- Optimization Detective uses this to insert the
detect.js
script to the page. - Embed Optimizer adding a
SCRIPT
to the end of theBODY
when there is a lazy-loaded embed on the page. - Image Prioritizer adding a
SCRIPT
tag via a tag visitor to lazy load background images. - Image Prioritizer adding a
SCRIPT
tag 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 aMETA
tag::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_Processor
to 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_Processor
andWP_HTML_Processor
implementations. Namely, forWP_HTML_Processor
there 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_Processor
orWP_HTML_Processor
. In fact, this could allow Optimization Detective to use an unsubclassed originalWP_HTML_Tag_Processor
instance 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