namespace Elementor\Includes\Elements;
use Elementor\Controls_Manager;
use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
use Elementor\Element_Base;
use Elementor\Group_Control_Background;
use Elementor\Group_Control_Border;
use Elementor\Group_Control_Box_Shadow;
use Elementor\Group_Control_Css_Filter;
use Elementor\Group_Control_Flex_Container;
use Elementor\Group_Control_Flex_Item;
use Elementor\Group_Control_Grid_Container;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
class Container extends Element_Base {
* @var \Elementor\Core\Kits\Documents\Kit
protected function is_dynamic_content(): bool {
* @param array|null $args
public function __construct( array $data = [], array $args = null ) {
parent::__construct( $data, $args );
$this->active_kit = Plugin::$instance->kits_manager->get_active_kit();
public static function get_type() {
public function get_name() {
* Get the element display name.
public function get_title() {
return esc_html__( 'Container', 'elementor' );
* Get the element display icon.
public function get_icon() {
return 'eicon-container';
public function get_keywords() {
return [ 'Container', 'Flex', 'Flexbox', 'Flexbox Container', 'Grid', 'Grid Container', 'CSS Grid', 'Layout' ];
public function get_panel_presets() {
'name' => 'container_grid',
'container_type' => [ 'default' => 'grid' ],
'title' => esc_html__( 'Grid', 'elementor' ),
'icon' => 'eicon-container-grid',
'originalWidget' => $this->get_name(),
'presetWidget' => 'container_grid',
'container_type' => 'grid',
'presetTitle' => esc_html__( 'Grid', 'elementor' ),
'presetIcon' => 'eicon-container-grid',
* Override the render attributes to add a custom wrapper class.
protected function add_render_attributes() {
parent::add_render_attributes();
$is_nested_class_name = $this->get_data( 'isInner' ) ? 'e-child' : 'e-parent';
$this->add_render_attribute( '_wrapper', [
* Override the initial element config to display the Container in the panel.
protected function get_initial_config() {
$config = parent::get_initial_config();
$config['controls'] = $this->get_controls();
$config['tabs_controls'] = $this->get_tabs_controls();
$config['show_in_panel'] = true;
$config['categories'] = [ 'layout' ];
* Render the element JS template.
protected function content_template() {
<# if ( 'boxed' === settings.content_width ) { #>
<div class="e-con-inner">
if ( settings.background_video_link ) {
let videoAttributes = 'autoplay muted playsinline';
if ( ! settings.background_play_once ) {
videoAttributes += ' loop';
'background-video-container',
'class': 'elementor-background-video-container',
if ( ! settings.background_play_on_mobile ) {
view.addRenderAttribute( 'background-video-container', 'class', 'elementor-hidden-mobile' );
<div {{{ view.getRenderAttributeString( 'background-video-container' ) }}}>
<div class="elementor-background-video-embed"></div>
<video class="elementor-background-video-hosted" {{ videoAttributes }}></video>
<div class="elementor-shape elementor-shape-top" aria-hidden="true"></div>
<div class="elementor-shape elementor-shape-bottom" aria-hidden="true"></div>
<# if ( 'boxed' === settings.content_width ) { #>
* Render the video background markup.
protected function render_video_background() {
$settings = $this->get_settings_for_display();
if ( 'video' !== $settings['background_background'] ) {
if ( ! $settings['background_video_link'] ) {
$video_properties = Embed::get_video_properties( $settings['background_video_link'] );
$this->add_render_attribute(
'background-video-container',
'class' => 'elementor-background-video-container',
if ( ! $settings['background_play_on_mobile'] ) {
$this->add_render_attribute( 'background-video-container', 'class', 'elementor-hidden-mobile' );
?><div <?php $this->print_render_attribute_string( 'background-video-container' ); ?>>
<?php if ( $video_properties ) : ?>
<div class="elementor-background-video-embed"></div>
$video_tag_attributes = 'autoplay muted playsinline';
if ( 'yes' !== $settings['background_play_once'] ) {
$video_tag_attributes .= ' loop';
<video class="elementor-background-video-hosted" <?php echo esc_attr( $video_tag_attributes ); ?>></video>
* Render the Container's shape divider.
* TODO: Copied from `section.php`.
* Used to generate the shape dividers HTML.
* @param string $side - Shape divider side, used to set the shape key.
protected function render_shape_divider( $side ) {
$settings = $this->get_active_settings();
$base_setting_key = "shape_divider_$side";
$negative = ! empty( $settings[ $base_setting_key . '_negative' ] );
$shape_path = Shapes::get_shape_path( $settings[ $base_setting_key ], $negative );
if ( ! is_file( $shape_path ) || ! is_readable( $shape_path ) ) {
<div class="elementor-shape elementor-shape-<?php echo esc_attr( $side ); ?>" aria-hidden="true" data-negative="<?php
Utils::print_unescaped_internal_string( $negative ? 'true' : 'false' );
// PHPCS - The file content is being read from a strict file path structure.
echo Utils::file_get_contents( $shape_path ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
* Print safe HTML tag for the element based on the element settings.
protected function print_html_tag() {
$html_tag = $this->get_settings( 'html_tag' );
if ( empty( $html_tag ) ) {
Utils::print_validated_html_tag( $html_tag );
* Before rendering the container content. (Print the opening tag, etc.)
public function before_render() {
$settings = $this->get_settings_for_display();
$link = $settings['link'];
if ( ! empty( $link['url'] ) ) {
$this->add_link_attributes( '_wrapper', $link );
?><<?php $this->print_html_tag(); ?> <?php $this->print_render_attribute_string( '_wrapper' ); ?>>
if ( $this->is_boxed_container( $settings ) ) { ?>
<div class="e-con-inner">
$this->render_video_background();
if ( ! empty( $settings['shape_divider_top'] ) ) {
$this->render_shape_divider( 'top' );
if ( ! empty( $settings['shape_divider_bottom'] ) ) {
$this->render_shape_divider( 'bottom' );
* After rendering the Container content. (Print the closing tag, etc.)
public function after_render() {
$settings = $this->get_settings_for_display();
if ( $this->is_boxed_container( $settings ) ) { ?>
</<?php $this->print_html_tag(); ?>>
protected function is_boxed_container( array $settings ) {
return ! empty( $settings['content_width'] ) && 'boxed' === $settings['content_width'];
* Override the default child type to allow widgets & containers as children.
* @param array $element_data
* @return \Elementor\Element_Base|\Elementor\Widget_Base|null
protected function _get_default_child_type( array $element_data ) {
$el_types = array_keys( Plugin::$instance->elements_manager->get_element_types() );
if ( in_array( $element_data['elType'], $el_types, true ) ) {
return Plugin::$instance->elements_manager->get_element_types( $element_data['elType'] );
return Plugin::$instance->widgets_manager->get_widget_types( $element_data['widgetType'] );
* Register the Container's layout controls.
protected function register_container_layout_controls() {
$this->start_controls_section(
'section_layout_container',
'label' => esc_html__( 'Container', 'elementor' ),
'tab' => Controls_Manager::TAB_LAYOUT,
$active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
if ( array_key_exists( Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA, $active_breakpoints ) ) {
$min_affected_device = Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA;
$min_affected_device = Breakpoints_Manager::BREAKPOINT_KEY_TABLET;
'label' => esc_html__( 'Container Layout', 'elementor' ),
'type' => Controls_Manager::SELECT,
'flex' => esc_html__( 'Flexbox', 'elementor' ),
'grid' => esc_html__( 'Grid', 'elementor' ),
'{{WRAPPER}}' => '--display: {{VALUE}}',
'editor_available' => true,
'label' => esc_html__( 'Content Width', 'elementor' ),
'type' => Controls_Manager::SELECT,
'boxed' => esc_html__( 'Boxed', 'elementor' ),
'full' => esc_html__( 'Full Width', 'elementor' ),
'render_type' => 'template',
'prefix_class' => 'e-con-',
'editor_available' => true,
$width_control_settings = [
'label' => esc_html__( 'Width', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'min_affected_device' => [
Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => $min_affected_device,
Breakpoints_Manager::BREAKPOINT_KEY_LAPTOP => $min_affected_device,
Breakpoints_Manager::BREAKPOINT_KEY_TABLET_EXTRA => $min_affected_device,
Breakpoints_Manager::BREAKPOINT_KEY_TABLET => $min_affected_device,
Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA => $min_affected_device,
$this->add_responsive_control(
array_merge( $width_control_settings, [
'{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};',
'content_width' => 'full',
Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
Breakpoints_Manager::BREAKPOINT_KEY_MOBILE => [
// The mobile width is not inherited from the higher breakpoint width controls.
$this->add_responsive_control(
array_merge( $width_control_settings, [
'{{WRAPPER}}' => '--content-width: {{SIZE}}{{UNIT}};',
'content_width' => 'boxed',
Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
// Use the default width from the kit as a placeholder.
'placeholder' => $this->active_kit->get_settings_for_display( 'container_width' ),
Breakpoints_Manager::BREAKPOINT_KEY_MOBILE => [
// The mobile width is not inherited from the higher breakpoint width controls.
$this->add_responsive_control(
'label' => esc_html__( 'Min Height', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ],
'description' => sprintf(
esc_html__( 'To achieve full height Container use %s.', 'elementor' ),
'{{WRAPPER}}' => '--min-height: {{SIZE}}{{UNIT}};',