Elementor – популярный wp плагин для построения страниц; данный тип плагинов называют pagebuilder. Это плагин(как и все в его категории) оперирует специальными виджетами – именно его я и создам, по ходу статьи…
Содержание
Предисловие
Здесья расскажу о том, что толкнуло меня на создание виджета(помимо получения опыта для написания этой писульки). Дело было так: была страница на которую выводилось видео с YouTube. К нему был список тайм-кодов, клик по которым включал воспроизведение видео с определенного момента. Временная метка(таймкод) состоит из самого времени, картинки и текста-описания. При переезде страницы на Элементор – всё взаимодействие сламалось. Задача: создать аддон к Элементор, добавляющий эти возможности в виджете.
Немного воды…
Всем, кто знаком с веб-программированием, с ходу стало понятно в чем причина – javascript; теперь для тех, кто незнаком: за прослушивание всех событий в браузере отвечает javascript. И правда, билдер изменил структуру DOM-дерева страницы и, за часик(долго разбирался в новой структуре), изменив селекторы в скрипте все заработало…
И что ж?.. на этом все? Было бы всё, если бы владелец не сказал, что неудобно так: строить ряд, в нем колонки с текстовым полем в(которых снова ряды с колонками) – сделай-ка единым блоком, чтоб в элементы управления просто вбить значения.
Приступим…
Начал я, разумеется, с оболочки плагина. На сайте плагина, в доках, есть шаблон кода основного файла для аддонов Элементор. Немного опишу главный метод, который в нем присутствует
add_action('elementor/init', function () {
add_action('elementor/widgets/widgets_registered', function () {
require_once(__DIR__ . '/class-youtube-widget.php');
ElementorPlugin::instance()->widgets_manager->register_widget_type(new Elementor_YouTube_Widget());
});
});чтоб написать любое расширение к элементору нужно расширить его класс; чтобы получить доступ к ядру плагина(а именно там лежат они) нужно вызывать аддон после загрузки плагина. В движке есть хук(plugins_loaded), срабатывающий после загрузки всех плагинов – однако, в виду того, как устроен сам Элементор – в этот момент его ядро уже недоступно.
Плагин Элементор имеет свои хуки, позволяющие получить доступ к разным классам его ядра:
elementor/init– ко всем;ementor/widgets/widgets_registered– к классуElementorWidget_Base(и прочим, необходимым для создания нового виджета);elementor/controls/control_registered– к классуElementorControl_Base(и прочим, необходимым для создания нового типа контролера для плагина);
Это все хуки, которые нужно знать, для того чтоб построить свой виджет. На деле, из-за довольно широкой палитры встроенных контролей, 3ий хук я никогда не использовал – не было нужды создавать свои контроллеры.
Теперь можно переходить к созданию самого виджета… хотя, нет – пару строк о скрипте. Здесь, как и в вордпресс, нет метода, который подключал бы скрипты только когда виджет есть на странице(по крайней мере я таких не увидел – если ты о них знаешь – напиши в комментах, буду благодарен!) и потому я подключаю их как обычные скрипты.
Теперь немного о самом скрипте – в нем я буду использовать YouTube Iframe Player API. Начнем с html-разметки, которую будем строить на странице
<section>
<h2>{{ section_title }}</h2>
<div class="elementor-row {{ video_position }}" data-id="{{ video_id }}">
<div class="elementor-column frame-container">
<img src="https://img.youtube.com/vi/{{ video_id }}/hqdefault.jpg" class="youtube-preview" alt="Video Preview">
</div>
<div class="elementor-column">
{{ timestamps_loop }}
<figure class="stamp">
<img src="{{ timestamp_image_url }}" alt="Stamp Icon">
<span class="timestamp">{{ timestamp_time }}</span>
<span class="description">{{ timestamp_description }}</span>
</igure>
{{ endloop }}
</div>
</div>
</section>Думал использовать Iframe API, но глянув, что он делает – послал его лесом. Дело в том, что беглый анализ кода показал, что апи содержит методы постройки тега iframe с его атрибутами; учитывая, что страница индексируемая(должна быть оптимизирована под скорость загрузки) я решил не захламлять ее дополнительной библиотекой(jquery и так хорошо выполняет эту задачу). В общем, вот скрипт для работы с метками с вышеприведенной разметкой
(function($) {
'use strict';
$(document).on('click', '.youtube-preview', function() {
var id = $(this).closest('.elementor-row').data('id');
YTFrame.build(id, 0, $(this).parent());
});
$(document).on('click', 'figure.stamp', function() {
console.log(this);
var id = $(this).closest('.elementor-row').data('id'),
time = $(this).find('.timestamp').text().split(':'),
seconds = 60 * parseInt(time[0]) + parseInt(time[1]);
console.log($(this).closest('.elementor-row').find('.frame-container')[0]);
YTFrame.build(id, seconds, $(this).closest('.elementor-row').find('.frame-container')[0]);
});
var YTFrame = {
build: function(id, seconds, container) {
var iframe = document.createElement('iframe');
iframe.src = 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1&start=' + seconds;
$(container).html(iframe);
}
}
})(jQuery);Также, данный скрипт позволил обойти некоторые ограничения.
Доделываем
Имея скрипт и накидав разметку, а также оболочку для аддона – создадим класс нашего виджета. Он является дочерним для базового абстрактного виджета элементор(который доступен только в ядре плагина); также, необходимы классы для контроля виджета в редакторе
<?php use ElementorControls_Manager; use ElementorWidget_Base; use ElementorUtils; use ElementorRepeater;
Теперь создадмим сам класс со следующими методами
<?php
class Elementor_Test_Widget extends ElementorWidget_Base {
public function get_name() {}
public function get_title() {}
public function get_icon() {}
public function get_categories() {}
protected function _register_controls() {}
protected function render() {}
protected function _content_template() {}
}Первые 4 методы просто возвращают по строке. Метод render() строит разметку в редакторе(с приметкой) и на сайте
protected function render()
{
$settings = $this->get_settings_for_display();
$this->add_inline_editing_attributes('title', 'advanced'); ?>
<section>
<h2 <?php echo $this->get_render_attribute_string('text_attr') ?>>
<?php echo $this->get_settings('title') ?>
</h2>
<div class="elementor-row <?php echo esc_attr($settings['video_position']) ?>" data-id="<?php echo esc_attr($settings['video_id']) ?>">
<div class="elementor-column frame-container">
<img src="<?php printf('https://img.youtube.com/vi/%s/hqdefault.jpg', esc_attr($settings['video_id'])) ?>" class="youtube-preview" alt="Video Preview">
</div>
<div class="elementor-column">
<?php foreach ($settings['timestams_repeater'] as $key => $value): ?>
<figure class="stamp">
<img src="<?php echo esc_url($value['stamp_image']['ur']) ?>" alt="Stamp Icon">
<span class="timestamp"><?php echo esc_html($value['time_stamp']) ?></span>
<span class="description"><?php echo esc_html($value['stamp_description']) ?></span>
</igure>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
}Здесь Widget_Base->get_settings_for_display() это получение настроек виджета, которые я создам в следующем методе.
Метод _register_controls() отвечает за создание контролей виджета в сайдбаре редактора – здесь немного поподробней. Все настройки растоложены в 3х табах – контент, стиль и дополнительные(их не трожь – они всегда дефолтные!). чтоб выбрать таб нужно в этом методе написать
$this->start_controls_section(
'content_section',
[
'label' => __('Video'),
'tab' => Controls_Manager::TAB_CONTENT,
]
);затем следует открывающий и закрывающий секцию настроек код
$this->add_control(
'title',
[
'label' => __('Section Title'),
'type' => Controls_Manager::TEXT,
'input_type' => 'text'
]
);
$this->end_controls_section();и уже между этими тегами можно писать свою настройку.
Вся “слоенка” будет выглядеть так
// указание на таб
// если TAB_CONTENT поменять на TAB_STYLE
// будет запись в стиль-таб
$this->start_controls_section(
'content_section',
[
'label' => __('Video'),
'tab' => Controls_Manager::TAB_CONTENT,
]
);
// открытие новой секции
$this->add_control(
'title',
[
'label' => __('Section Title'),
'type' => Controls_Manager::TEXT,
'input_type' => 'text'
]
);
// настройка в секции, их может быть много
$this->add_control(
'video_id',
[
'label' => __('Video ID'),
'type' => Controls_Manager::TEXT,
'input_type' => 'text'
]
);
// закрытие текущей секции, но не таба!
// после этого можно открыть новую секцию
// она будет в том же табе
$this->end_controls_section();Впринципе, этих методов достаточно для того, чтоб сделать рабочий виджет. Далее пойдет вещь, которую я не делал в этой работе, но хочу показать и рассказать.
Наверняка внимательный читатель не только статьи но и кода, заметил в рендеринг-методе эти строки
$this->add_inline_editing_attributes('title', 'advanced');
$this->get_render_attribute_string('title');
$this->get_settings('title');все они не имеют смысла без метода _content_template(). Поясню: виджет элементор – блок визуально редактируемый; это значит его можно редактировать в поле редактора, а не в сайдбаре. Кароч, 1ая строка это указатель на то, что эта настройка имееит возможность визуального редактирования; 2ой – получает набор классов для того чтоб javascript элементора взял под контроль этот тег; 3я – получает само значение настройки. В случаи с ‘advanced’ можно увидеть расширенную панель редактирования текста

Однако, чтоб все это сработало – необходим вышеуказанный метод с следующими строками
<# view.addInlineEditingAttributes( 'title', 'advanced' ); #>
<div {{{ view.getRenderAttributeString( 'title' ) }}}>{{{ settings.title }}}</div>Заметь(!) эти строки должны выводиться, тк они будут вставлены в backbone-шаблоны(документация по ним). 2ая строка это т, что в окне редактора будет вставлено вместо редактируемой строки.
Заключение
Было разработанное расширение для Элементор. Код сможешь глянуть здесь, конечно он не полный(я удалил из него интеграции с другими плагинами, кастомными плагинами и сократил дерево настроек). Больше примеров ты увидишь в самом Элементор, ну а я попытался немного объяснить – что и как там работает.

