From bc2a1e93fb79362173dd2edcf8540640516e2dc2 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev <92088692+rami-elementor@users.noreply.github.com> Date: Mon, 16 Dec 2024 06:38:27 -0800 Subject: [PATCH] Docs: Add documentation for Widget Optimization [ED-16419] (#298) --- src/.vuepress/sidebars/widgets.js | 8 +++ src/controls/complex-example.md | 26 ++++++++ src/controls/simple-example.md | 26 ++++++++ src/widgets/advanced-example.md | 26 ++++++++ src/widgets/simple-example.md | 26 ++++++++ src/widgets/widget-inner-wrapper.md | 94 +++++++++++++++++++++++++++++ src/widgets/widget-optimization.md | 23 +++++++ src/widgets/widget-structure.md | 2 + 8 files changed, 231 insertions(+) create mode 100644 src/widgets/widget-inner-wrapper.md create mode 100644 src/widgets/widget-optimization.md diff --git a/src/.vuepress/sidebars/widgets.js b/src/.vuepress/sidebars/widgets.js index 10d62330..bc72cfc0 100644 --- a/src/.vuepress/sidebars/widgets.js +++ b/src/.vuepress/sidebars/widgets.js @@ -46,6 +46,14 @@ module.exports = [ 'rendering-repeaters', 'rendering-html-attribute', 'rendering-inline-editing', + ] + }, + 'widget-optimization', + { + collapsable: false, + sidebarDepth: -1, + children: [ + 'widget-inner-wrapper', 'widget-output-caching', ] }, diff --git a/src/controls/complex-example.md b/src/controls/complex-example.md index 73c5cffc..6e836ecf 100644 --- a/src/controls/complex-example.md +++ b/src/controls/complex-example.md @@ -314,6 +314,32 @@ class Elementor_Test_Widget extends \Elementor\Widget_Base { return 'https://developers.elementor.com/docs/widgets/'; } + /** + * Whether the widget requires inner wrapper. + * + * Determine whether to optimize the DOM size. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to optimize the DOM size. + */ + public function has_widget_inner_wrapper(): bool { + return false; + } + + /** + * Whether the element returns dynamic content. + * + * Determine whether to cache the element output or not. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to cache the element output. + */ + protected function is_dynamic_content(): bool { + return false; + } + /** * Register test widget controls. * diff --git a/src/controls/simple-example.md b/src/controls/simple-example.md index 75b43fce..300cf393 100644 --- a/src/controls/simple-example.md +++ b/src/controls/simple-example.md @@ -291,6 +291,32 @@ class Elementor_Currency_Widget extends \Elementor\Widget_Base { return 'https://developers.elementor.com/docs/widgets/'; } + /** + * Whether the widget requires inner wrapper. + * + * Determine whether to optimize the DOM size. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to optimize the DOM size. + */ + public function has_widget_inner_wrapper(): bool { + return false; + } + + /** + * Whether the element returns dynamic content. + * + * Determine whether to cache the element output or not. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to cache the element output. + */ + protected function is_dynamic_content(): bool { + return false; + } + /** * Register currency widget controls. * diff --git a/src/widgets/advanced-example.md b/src/widgets/advanced-example.md index 3e501afb..bfef107e 100644 --- a/src/widgets/advanced-example.md +++ b/src/widgets/advanced-example.md @@ -176,6 +176,32 @@ class Elementor_List_Widget extends \Elementor\Widget_Base { ]; } + /** + * Whether the widget requires inner wrapper. + * + * Determine whether to optimize the DOM size. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to optimize the DOM size. + */ + public function has_widget_inner_wrapper(): bool { + return false; + } + + /** + * Whether the element returns dynamic content. + * + * Determine whether to cache the element output or not. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to cache the element output. + */ + protected function is_dynamic_content(): bool { + return false; + } + /** * Register list widget controls. * diff --git a/src/widgets/simple-example.md b/src/widgets/simple-example.md index c3a0da43..7652fe15 100644 --- a/src/widgets/simple-example.md +++ b/src/widgets/simple-example.md @@ -155,6 +155,32 @@ class Elementor_oEmbed_Widget extends \Elementor\Widget_Base { return 'https://developers.elementor.com/docs/widgets/'; } + /** + * Whether the widget requires inner wrapper. + * + * Determine whether to optimize the DOM size. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to optimize the DOM size. + */ + public function has_widget_inner_wrapper(): bool { + return false; + } + + /** + * Whether the element returns dynamic content. + * + * Determine whether to cache the element output or not. + * + * @since 1.0.0 + * @access protected + * @return bool Whether to cache the element output. + */ + protected function is_dynamic_content(): bool { + return false; + } + /** * Register oEmbed widget controls. * diff --git a/src/widgets/widget-inner-wrapper.md b/src/widgets/widget-inner-wrapper.md new file mode 100644 index 00000000..c75f6d16 --- /dev/null +++ b/src/widgets/widget-inner-wrapper.md @@ -0,0 +1,94 @@ +# Widget DOM Optimization + + + +Elementor widgets define their own markup in the `render()` method. However, Elementor wraps each widget in two `
` elements; the outer `
` element, and the inner `
` element. These additional wrappers allow Elementor to add additional styles like background, margins, borders, motion effects, etc. + +Two wrappers for each widget increases the overall DOM size, reducing page performance. To overcome this, developers can use the `has_widget_inner_wrapper()` method to control the number of wrapper elements the widget has. + +By switching to a single wrapper, a widget can reduce the DOM size and optimize its footprint on the page. However, existing widgets that rely on the inner `.elementor-widget-container` wrapping element to style widgets, can maintain backwards compatibility. + +## Widget Markup + +The current, unoptimized widget markup, includes two wrapping elements: + +```html +
+
+ ... +
+
+``` + +The optimized markup has only one wrapping element: + +```html +
+ ... +
+``` + +By default, Elementor uses the unoptimized markup for backwards compatibility. + +## Examples + +### Optimized Widget DOM + +To reduce the DOM size, developers can use the `has_widget_inner_wrapper()` method in the widget class, as shown below: + +```php +` wrapper. + +### Retaining Unoptimized Widget DOM (for BC) + +Legacy widgets that rely on the `.elementor-widget-container` class can continue using the unoptimized DOM by setting the method to return `true`: + +```php{4-6,16} +add_control( + 'color', + [ + 'label' => esc_html__( 'Color', 'textdomain' ), + 'type' => \Elementor\Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} > .elementor-widget-container h3' => 'color: {{VALUE}};', + ], + ] + ); + } + + protected function render(): void { + ?> +

+ ... +

+ + +Elementor offers several methods and best practices to optimize widgets and improve performance. The optimizations are not implemented for all widgets in order to maintain backwards compatibility. In addition, each widget has its own characteristics. The widget developer needs to add some extra methods to the widget class to make sure Elementor knows how to process it. + +## Performance Optimization Methods + +Elementor widgets has two performance optimization methods: + +```php +class Elementor_Test_Widget extends \Elementor\Widget_Base { + + public function has_widget_inner_wrapper(): bool {} + + protected function is_dynamic_content(): bool {} + +} +``` + +* **DOM Optimization** - The `has_widget_inner_wrapper()` method lets you determine whether the widget uses optimized DOM structure or not. + +* **Element Output Caching** - The `is_dynamic_content()` method lets you determine whether the widget returns dynamic content, to cache the element HTML output or not. diff --git a/src/widgets/widget-structure.md b/src/widgets/widget-structure.md index 6d823bbe..b192f5ad 100644 --- a/src/widgets/widget-structure.md +++ b/src/widgets/widget-structure.md @@ -40,6 +40,8 @@ class Elementor_Test_Widget extends \Elementor\Widget_Base { public function get_style_depends(): array {} + public function has_widget_inner_wrapper(): bool {} + protected function is_dynamic_content(): bool {} protected function register_controls(): void {}