* @return self Current instance of the element.
public function set_render_attribute( $element, $key = null, $value = null ) {
return $this->add_render_attribute( $element, $key, $value, true );
* Remove render attribute.
* Used to remove an element (with its keys and their values), key (with its values),
* or value/s from an HTML element's render attribute.
* @param string $element The HTML element.
* @param string $key Optional. Attribute key. Default is null.
* @param array|string $values Optional. Attribute value/s. Default is null.
public function remove_render_attribute( $element, $key = null, $values = null ) {
if ( $key && ! isset( $this->render_attributes[ $element ][ $key ] ) ) {
$values = (array) $values;
$this->render_attributes[ $element ][ $key ] = array_diff( $this->render_attributes[ $element ][ $key ], $values );
unset( $this->render_attributes[ $element ][ $key ] );
if ( isset( $this->render_attributes[ $element ] ) ) {
unset( $this->render_attributes[ $element ] );
* Get render attribute string.
* Used to retrieve the value of the render attribute.
* @param string $element The element.
* @return string Render attribute string, or an empty string if the attribute
public function get_render_attribute_string( $element ) {
if ( empty( $this->render_attributes[ $element ] ) ) {
return Utils::render_html_attributes( $this->render_attributes[ $element ] );
* Print render attribute string.
* Used to output the rendered attribute.
* @param array|string $element The element.
public function print_render_attribute_string( $element ) {
Utils::print_unescaped_internal_string( $this->get_render_attribute_string( $element ) );
* Print element template.
* Used to generate the element template on the editor.
public function print_template() {
// TODO: This is for backwards compatibility starting from 2.9.0
// This `if` statement should be removed when the method is removed.
if ( $this->has_own_method( '_content_template', self::class ) ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_content_template', '2.9.0', __CLASS__ . '::content_template()' );
$this->_content_template();
$this->content_template();
$template_content = ob_get_clean();
$element_type = $this->get_type();
* Filters the controls stack template content before it's printed in the editor.
* The dynamic portion of the hook name, `$element_type`, refers to the element type.
* @param string $content_template The controls stack template in the editor.
* @param Controls_Stack $this The controls stack.
$template_content = apply_filters( "elementor/{$element_type}/print_template", $template_content, $this );
if ( empty( $template_content ) ) {
<script type="text/html" id="tmpl-elementor-<?php echo esc_attr( $this->get_name() ); ?>-content">
<?php $this->print_template_content( $template_content ); ?>
* On import update dynamic content (e.g. post and term IDs).
* @param array $config The config of the passed element.
* @param array $data The data that requires updating/replacement when imported.
* @param array|null $controls The available controls.
* @return array Element data.
public static function on_import_update_dynamic_content( array $config, array $data, $controls = null ): array {
* Used to inject controls and sections to a specific position in the stack.
* When you use this method, all the registered controls and sections will
* be injected to a specific position in the stack, until you stop the
* injection using `end_injection()` method.
* @param array $position {
* The position where to start the injection.
* @type string $type Injection type, either `control` or `section`.
* @type string $at Where to inject. If `$type` is `control` accepts
* `before` and `after`. If `$type` is `section`
* accepts `start` and `end`. Default values based on
* @type string $of Control/Section ID.
final public function start_injection( array $position ) {
if ( $this->injection_point ) {
wp_die( 'A controls injection is already opened. Please close current injection before starting a new one (use `end_injection`).' );
$this->injection_point = $this->get_position_info( $position );
* Used to close an existing opened injection point.
* When you use this method it stops adding new controls and sections to
* this point and continue to add controls to the regular position in the
final public function end_injection() {
$this->injection_point = null;
* Retrieve the injection point in the stack where new controls and sections
* @return array|null An array when an injection point is defined, null
final public function get_injection_point() {
return $this->injection_point;
* Used to add new controls to any element type. For example, external
* developers use this method to register controls in a widget.
* Should be inherited and register new controls using `add_control()`,
* `add_responsive_control()` and `add_group_control()`, inside control
* wrappers like `start_controls_section()`, `start_controls_tabs()` and
* `start_controls_tab()`.
* @deprecated 3.1.0 Use `register_controls()` method instead.
protected function _register_controls() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', 'register_controls()' );
$this->register_controls();
* Used to add new controls to any element type. For example, external
* developers use this method to register controls in a widget.
* Should be inherited and register new controls using `add_control()`,
* `add_responsive_control()` and `add_group_control()`, inside control
* wrappers like `start_controls_section()`, `start_controls_tabs()` and
* `start_controls_tab()`.
protected function register_controls() {}
* Retrieve the default data. Used to reset the data on initialization.
* @return array Default data.
protected function get_default_data() {
protected function get_init_settings() {
$settings = $this->get_data( 'settings' );
$controls_objs = Plugin::$instance->controls_manager->get_controls();
foreach ( $this->get_controls() as $control ) {
$control_obj = $controls_objs[ $control['type'] ] ?? null;
if ( ! $control_obj instanceof Base_Data_Control ) {
$control = array_merge_recursive( $control_obj->get_settings(), $control );
$settings[ $control['name'] ] = $control_obj->get_value( $control, $settings );
* Retrieve the current element initial configuration - controls list and
* the tabs assigned to the control.
* @return array The initial config.
protected function get_initial_config() {
'controls' => $this->get_controls(),
* Retrieve the current element initial configuration - controls list and
* the tabs assigned to the control.
* @deprecated 2.9.0 Use `get_initial_config()` method instead.
* @return array The initial config.
protected function _get_initial_config() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '2.9.0', 'get_initial_config()' );
return $this->get_initial_config();
* Retrieve the section arguments based on section ID.
* @param string $section_id Section ID.
* @return array Section arguments.
protected function get_section_args( $section_id ) {
$section_control = $this->get_controls( $section_id );
$section_args_keys = [ 'tab', 'condition' ];
$args = array_intersect_key( $section_control, array_flip( $section_args_keys ) );
$args['section'] = $section_id;
* Generates the final HTML on the frontend.
protected function render() {}
* Render element in static mode.
* If not inherent will call the base render.
protected function render_static() {
* Determine the render logic.
protected function render_by_mode() {
if ( Plugin::$instance->frontend->is_static_render_mode() ) {
* Print content template.
* Used to generate the content template on the editor, using a
* Backbone JavaScript template.
* @param string $template_content Template content.
protected function print_template_content( $template_content ) {
Utils::print_unescaped_internal_string( $template_content );
* Render element output in the editor.
* Used to generate the live preview, using a Backbone JavaScript template.
protected function content_template() {}
* Render element output in the editor.
* Used to generate the live preview, using a Backbone JavaScript template.
* @deprecated 2.9.0 Use `content_template()` method instead.
protected function _content_template() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '2.9.0', 'content_template()' );
$this->content_template();
* Register the all controls added by `register_controls()`.
protected function init_controls() {
Plugin::$instance->controls_manager->open_stack( $this );
// TODO: This is for backwards compatibility starting from 2.9.0
// This `if` statement should be removed when the method is removed.
if ( $this->has_own_method( '_register_controls', self::class ) ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_register_controls', '3.1.0', __CLASS__ . '::register_controls()' );
$this->_register_controls();
$this->register_controls();
protected function handle_control_position( array $args, $control_id, $overwrite ) {
if ( isset( $args['type'] ) && in_array( $args['type'], [ Controls_Manager::SECTION, Controls_Manager::WP_WIDGET ], true ) ) {
$target_section_args = $this->current_section;
$target_tab = $this->current_tab;
if ( $this->injection_point ) {
$target_section_args = $this->injection_point['section'];
if ( ! empty( $this->injection_point['tab'] ) ) {
$target_tab = $this->injection_point['tab'];
if ( null !== $target_section_args ) {
if ( ! empty( $args['section'] ) || ! empty( $args['tab'] ) ) {
_doing_it_wrong( sprintf( '%s::%s', get_called_class(), __FUNCTION__ ), sprintf( 'Cannot redeclare control with `tab` or `section` args inside section "%s".', $control_id ), '1.0.0' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$args = array_replace_recursive( $target_section_args, $args );
if ( null !== $target_tab ) {
$args = array_replace_recursive( $target_tab, $args );
} elseif ( empty( $args['section'] ) && ( ! $overwrite || is_wp_error( Plugin::$instance->controls_manager->get_control_from_stack( $this->get_unique_name(), $control_id ) ) ) ) {
if ( ! Performance::should_optimize_controls() ) {
wp_die( sprintf( '%s::%s: Cannot add a control outside of a section (use `start_controls_section`).', get_called_class(), __FUNCTION__ ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
* Set the raw data, the ID and the parsed settings.
* @param array $data Initial data.
protected function init( $data ) {
$this->data = array_merge( $this->get_default_data(), $data );
* Set the raw data, the ID and the parsed settings.
* @deprecated 2.9.0 Use `init()` method instead.
* @param array $data Initial data.