diff --git a/plugins/image-prioritizer/class-ip-img-tag-visitor.php b/plugins/image-prioritizer/class-ip-img-tag-visitor.php index c914673dff..99cd40e6b9 100644 --- a/plugins/image-prioritizer/class-ip-img-tag-visitor.php +++ b/plugins/image-prioritizer/class-ip-img-tag-visitor.php @@ -73,28 +73,15 @@ public function __invoke( OD_HTML_Tag_Walker $walker ): bool { } // TODO: Also if the element isLCPCandidate it should never by lazy-loaded. - $is_visible = false; - $is_found = false; - foreach ( $this->url_metrics_group_collection as $group ) { - foreach ( $group as $url_metric ) { - foreach ( $url_metric->get_elements() as $element ) { - if ( $xpath === $element['xpath'] ) { - $is_found = true; - if ( $element['intersectionRatio'] > 0 ) { - $is_visible = true; - break 3; // TODO: O(n^3) my! - } - } - } - } - } + $element_max_intersection_ratios = $this->url_metrics_group_collection->get_element_max_intersection_ratios(); // If the element was not found, we don't know if it was visible for not, so don't do anything. - if ( ! $is_found ) { + if ( ! array_key_exists( $xpath, $element_max_intersection_ratios ) ) { $walker->set_attribute( 'data-od-unknown-tag', true ); } else { // Otherwise, make sure visible elements omit the loading attribute, and hidden elements include loading=lazy. - $loading = (string) $walker->get_attribute( 'loading' ); + $is_visible = $element_max_intersection_ratios[ $xpath ] > 0.0; + $loading = (string) $walker->get_attribute( 'loading' ); if ( $is_visible && 'lazy' === $loading ) { $walker->set_attribute( 'data-od-removed-loading', $loading ); $walker->remove_attribute( 'loading' ); diff --git a/plugins/optimization-detective/class-od-url-metrics-group-collection.php b/plugins/optimization-detective/class-od-url-metrics-group-collection.php index 06e6339db7..7fa78f36a4 100644 --- a/plugins/optimization-detective/class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metrics-group-collection.php @@ -78,7 +78,8 @@ final class OD_URL_Metrics_Group_Collection implements Countable, IteratorAggreg * is_every_group_populated?: bool, * is_every_group_complete?: bool, * get_groups_by_lcp_element?: array, - * get_common_lcp_element?: ElementData|null + * get_common_lcp_element?: ElementData|null, + * get_element_max_intersection_ratios?: array * } */ private $result_cache = array(); @@ -370,6 +371,41 @@ public function get_common_lcp_element(): ?array { return $result; } + /** + * Gets the max intersection ratios of elements across all groups and their captured URL metrics. + * + * @return array Keys are XPaths and values are the intersection ratios. + */ + public function get_element_max_intersection_ratios(): array { + if ( array_key_exists( __FUNCTION__, $this->result_cache ) ) { + return $this->result_cache[ __FUNCTION__ ]; + } + + $result = ( function () { + $element_max_intersection_ratios = array(); + + /* + * O(n^3) my! Yes. This is why the result is cached. This being said, the number of groups should be 4 (one + * more than the default number of breakpoints) and the number of URL metrics for each group should be 3 + * (the default sample size). Therefore, given the number (n) of visited elements on the page this will only + * end up running n*4*3 times. + */ + foreach ( $this->groups as $group ) { + foreach ( $group as $url_metric ) { + foreach ( $url_metric->get_elements() as $element ) { + $element_max_intersection_ratios[ $element['xpath'] ] = array_key_exists( $element['xpath'], $element_max_intersection_ratios ) + ? max( $element_max_intersection_ratios[ $element['xpath'] ], $element['intersectionRatio'] ) + : $element['intersectionRatio']; + } + } + } + return $element_max_intersection_ratios; + } )(); + + $this->result_cache[ __FUNCTION__ ] = $result; + return $result; + } + /** * Gets URL metrics from all groups flattened into one list. *