Skip to content

Commit bc2a1e9

Browse files
Docs: Add documentation for Widget Optimization [ED-16419] (#298)
1 parent 4ccb3f1 commit bc2a1e9

File tree

8 files changed

+231
-0
lines changed

8 files changed

+231
-0
lines changed

src/.vuepress/sidebars/widgets.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ module.exports = [
4646
'rendering-repeaters',
4747
'rendering-html-attribute',
4848
'rendering-inline-editing',
49+
]
50+
},
51+
'widget-optimization',
52+
{
53+
collapsable: false,
54+
sidebarDepth: -1,
55+
children: [
56+
'widget-inner-wrapper',
4957
'widget-output-caching',
5058
]
5159
},

src/controls/complex-example.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,32 @@ class Elementor_Test_Widget extends \Elementor\Widget_Base {
314314
return 'https://developers.elementor.com/docs/widgets/';
315315
}
316316

317+
/**
318+
* Whether the widget requires inner wrapper.
319+
*
320+
* Determine whether to optimize the DOM size.
321+
*
322+
* @since 1.0.0
323+
* @access protected
324+
* @return bool Whether to optimize the DOM size.
325+
*/
326+
public function has_widget_inner_wrapper(): bool {
327+
return false;
328+
}
329+
330+
/**
331+
* Whether the element returns dynamic content.
332+
*
333+
* Determine whether to cache the element output or not.
334+
*
335+
* @since 1.0.0
336+
* @access protected
337+
* @return bool Whether to cache the element output.
338+
*/
339+
protected function is_dynamic_content(): bool {
340+
return false;
341+
}
342+
317343
/**
318344
* Register test widget controls.
319345
*

src/controls/simple-example.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,32 @@ class Elementor_Currency_Widget extends \Elementor\Widget_Base {
291291
return 'https://developers.elementor.com/docs/widgets/';
292292
}
293293

294+
/**
295+
* Whether the widget requires inner wrapper.
296+
*
297+
* Determine whether to optimize the DOM size.
298+
*
299+
* @since 1.0.0
300+
* @access protected
301+
* @return bool Whether to optimize the DOM size.
302+
*/
303+
public function has_widget_inner_wrapper(): bool {
304+
return false;
305+
}
306+
307+
/**
308+
* Whether the element returns dynamic content.
309+
*
310+
* Determine whether to cache the element output or not.
311+
*
312+
* @since 1.0.0
313+
* @access protected
314+
* @return bool Whether to cache the element output.
315+
*/
316+
protected function is_dynamic_content(): bool {
317+
return false;
318+
}
319+
294320
/**
295321
* Register currency widget controls.
296322
*

src/widgets/advanced-example.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,32 @@ class Elementor_List_Widget extends \Elementor\Widget_Base {
176176
];
177177
}
178178

179+
/**
180+
* Whether the widget requires inner wrapper.
181+
*
182+
* Determine whether to optimize the DOM size.
183+
*
184+
* @since 1.0.0
185+
* @access protected
186+
* @return bool Whether to optimize the DOM size.
187+
*/
188+
public function has_widget_inner_wrapper(): bool {
189+
return false;
190+
}
191+
192+
/**
193+
* Whether the element returns dynamic content.
194+
*
195+
* Determine whether to cache the element output or not.
196+
*
197+
* @since 1.0.0
198+
* @access protected
199+
* @return bool Whether to cache the element output.
200+
*/
201+
protected function is_dynamic_content(): bool {
202+
return false;
203+
}
204+
179205
/**
180206
* Register list widget controls.
181207
*

src/widgets/simple-example.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,32 @@ class Elementor_oEmbed_Widget extends \Elementor\Widget_Base {
155155
return 'https://developers.elementor.com/docs/widgets/';
156156
}
157157

158+
/**
159+
* Whether the widget requires inner wrapper.
160+
*
161+
* Determine whether to optimize the DOM size.
162+
*
163+
* @since 1.0.0
164+
* @access protected
165+
* @return bool Whether to optimize the DOM size.
166+
*/
167+
public function has_widget_inner_wrapper(): bool {
168+
return false;
169+
}
170+
171+
/**
172+
* Whether the element returns dynamic content.
173+
*
174+
* Determine whether to cache the element output or not.
175+
*
176+
* @since 1.0.0
177+
* @access protected
178+
* @return bool Whether to cache the element output.
179+
*/
180+
protected function is_dynamic_content(): bool {
181+
return false;
182+
}
183+
158184
/**
159185
* Register oEmbed widget controls.
160186
*
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Widget DOM Optimization
2+
3+
<Badge type="tip" vertical="top" text="Elementor Core" /> <Badge type="warning" vertical="top" text="Intermediate" />
4+
5+
Elementor widgets define their own markup in the `render()` method. However, Elementor wraps each widget in two `<div>` elements; the outer `<div class="elementor-widget">` element, and the inner `<div class="elementor-widget-container">` element. These additional wrappers allow Elementor to add additional styles like background, margins, borders, motion effects, etc.
6+
7+
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.
8+
9+
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.
10+
11+
## Widget Markup
12+
13+
The current, unoptimized widget markup, includes two wrapping elements:
14+
15+
```html
16+
<div class="elementor-widget elementor-widget-{widget-name}">
17+
<div class="elementor-widget-container">
18+
...
19+
</div>
20+
</div>
21+
```
22+
23+
The optimized markup has only one wrapping element:
24+
25+
```html
26+
<div class="elementor-widget elementor-widget-{widget-name}">
27+
...
28+
</div>
29+
```
30+
31+
By default, Elementor uses the unoptimized markup for backwards compatibility.
32+
33+
## Examples
34+
35+
### Optimized Widget DOM
36+
37+
To reduce the DOM size, developers can use the `has_widget_inner_wrapper()` method in the widget class, as shown below:
38+
39+
```php
40+
<?php
41+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
42+
43+
public function has_widget_inner_wrapper(): bool {
44+
return false;
45+
}
46+
47+
}
48+
```
49+
50+
This implementation instructs Elementor to render the widget with a single `<div>` wrapper.
51+
52+
### Retaining Unoptimized Widget DOM (for BC)
53+
54+
Legacy widgets that rely on the `.elementor-widget-container` class can continue using the unoptimized DOM by setting the method to return `true`:
55+
56+
```php{4-6,16}
57+
<?php
58+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
59+
60+
public function has_widget_inner_wrapper(): bool {
61+
return true;
62+
}
63+
64+
protected function register_controls(): void {
65+
66+
$this->add_control(
67+
'color',
68+
[
69+
'label' => esc_html__( 'Color', 'textdomain' ),
70+
'type' => \Elementor\Controls_Manager::COLOR,
71+
'selectors' => [
72+
'{{WRAPPER}} > .elementor-widget-container h3' => 'color: {{VALUE}};',
73+
],
74+
]
75+
);
76+
}
77+
78+
protected function render(): void {
79+
?>
80+
<h3>
81+
...
82+
</h3>
83+
<?php
84+
}
85+
}
86+
```
87+
88+
This widget can't use the optimized DOM capability as it uses the inner `.elementor-widget-container` CSS class to style the widget. Therefore, setting `has_widget_inner_wrapper()` to `true` will make sure that Elementor doesn't remove the inner wrapper for this widget.
89+
90+
## Notes
91+
92+
Please note that retaining unoptimized DOM is a temporary solution that allows addon developers to maintain compatibility while updating their code. The ultimate goal is to transition all widgets to use the optimized single-wrapper structure.
93+
94+
Optimized DOM for widget wrappers is not only setting `has_widget_inner_wrapper()` to `false`, it requires removal of `.elementor-widget-container` from all files, including PHP, CSS and JS.

src/widgets/widget-optimization.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Widget Optimization
2+
3+
<Badge type="tip" vertical="top" text="Elementor Core" /> <Badge type="warning" vertical="top" text="Intermediate" />
4+
5+
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.
6+
7+
## Performance Optimization Methods
8+
9+
Elementor widgets has two performance optimization methods:
10+
11+
```php
12+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
13+
14+
public function has_widget_inner_wrapper(): bool {}
15+
16+
protected function is_dynamic_content(): bool {}
17+
18+
}
19+
```
20+
21+
* **DOM Optimization** - The `has_widget_inner_wrapper()` method lets you determine whether the widget uses optimized DOM structure or not.
22+
23+
* **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.

src/widgets/widget-structure.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class Elementor_Test_Widget extends \Elementor\Widget_Base {
4040

4141
public function get_style_depends(): array {}
4242

43+
public function has_widget_inner_wrapper(): bool {}
44+
4345
protected function is_dynamic_content(): bool {}
4446

4547
protected function register_controls(): void {}

0 commit comments

Comments
 (0)