Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃悶 elementorModules.frontend.tools is undefined when creating a JS Handler for a Custom widget #19709

Open
5 tasks done
MarleyPlant opened this issue Sep 5, 2022 · 12 comments
Labels
bug Indicates a bug with one or multiple components. compatibility/assets Indicates a compatibility problem with scripts or CSS. mod/e* type/developer-api Indicates when a topic is related to the Developer API and Documentation.

Comments

@MarleyPlant
Copy link

MarleyPlant commented Sep 5, 2022

Prerequisites

  • I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
  • The issue still exists against the latest stable version of Elementor.

Description

When creating a JS Handler for a custom widget I am receiving the following error in the web browser.

Uncaught TypeError: Cannot read properties of undefined (reading 'tools')
    at Frontend.initOnReadyComponents (frontend.min.js?ver=3.7.4:formatted:308)
    at Frontend.init (frontend.min.js?ver=3.7.4:formatted:409)
    at HTMLDocument.<anonymous> (frontend.min.js?ver=3.7.4:formatted:418)
    at e (jquery.min.js?ver=3.6.0:formatted:1423)
    at t (jquery.min.js?ver=3.6.0:formatted:1434)
initOnReadyComponents @ frontend.min.js?ver=3.7.4:formatted:308
init @ frontend.min.js?ver=3.7.4:formatted:409
(anonymous) @ frontend.min.js?ver=3.7.4:formatted:418
e @ jquery.min.js?ver=3.6.0:formatted:1423
t @ jquery.min.js?ver=3.6.0:formatted:1434

the error seems to be caused when creating my handler class and using extends elementorModules.frontend.handlers.Base this handler is registered as a script depend for my widget in the widget class and also has "elementor-frontend" as a dependency when registering the script with WordPress

I can't seem to figure out the reason for this happening, I have followed the documentation exactly

Steps to reproduce

  1. Create custom widget
  2. Add Script Dependancy
  3. Create a Handler class that extends elementorModules.frontend.handlers.Base

Isolating the problem

  • This bug happens with only Elementor plugin active (and Elementor Pro).
  • This bug happens with a Blank WordPress theme active (Hello theme).
  • I can reproduce this bug consistently using the steps above.

System Info

Click to reveal
					
== Server Environment ==
	Operating System: Linux
	Software: nginx/1.16.0
	MySQL version: MySQL Community Server - GPL v8.0.16
	PHP Version: 7.4.1
	PHP Memory Limit: 256M
	PHP Max Input Vars: 4000
	PHP Max Post Size: 1000M
	GD Installed: Yes
	ZIP Installed: Yes
	Write Permissions: All right
	Elementor Library: Connected

== WordPress Environment ==
	Version: 6.0.2
	Site URL: http://elementorlibrary.local
	Home URL: http://elementorlibrary.local
	WP Multisite: No
	Max Upload Size: 300 MB
	Memory limit: 40M
	Max Memory limit: 256M
	Permalink Structure: /%postname%/
	Language: en-US
	Timezone: 0
	Debug Mode: Active

== Theme ==
	Name: Twenty Twenty-Two
	Version: 1.1
	Author: the WordPress team
	Child Theme: No

== User ==
	Role: administrator
	WP Profile lang: en_US
	User Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36

== Active Plugins ==
	Elementor
		Version: 3.7.4
		Author: Elementor.com

	Marleyplant Widget Library
		Version: 1.3.8
		Author: MarleyPlant


== Elements Usage ==
	
	wp-page : 1
		image-slider : 1


== Elementor Experiments ==
	Optimized DOM Output: Active by default
	Improved Asset Loading: Active by default
	Improved CSS Loading: Active by default
	Inline Font Icons: Inactive by default
	Accessibility Improvements: Active by default
	Additional Custom Breakpoints: Active by default
	Import Export Template Kit: Active by default
	Hide native WordPress widgets from search results: Active by default
	admin_menu_rearrangement: Inactive by default
	Flexbox Container: Inactive by default
	Landing Pages: Active by default
	Color Sampler: Active by default
	Favorite Widgets: Active by default
	Admin Top Bar: Active by default


== Log ==

Log: showing 5 of 52022-09-04 17:34:46 [info] elementor::elementor_updater Started 
2022-09-04 17:34:46 [info] Elementor/Upgrades - _on_each_version Start  
2022-09-04 17:34:46 [info] Elementor/Upgrades - _on_each_version Finished 
2022-09-04 17:34:46 [info] Elementor data updater process has been completed. [array (
  'plugin' => 'Elementor',
  'from' => '3.6.5',
  'to' => '3.7.4',
)]
2022-09-04 17:34:46 [info] Elementor data updater process has been queued. [array (
  'plugin' => 'Elementor',
  'from' => '3.6.5',
  'to' => '3.7.4',
)]

JS: showing 2 of 2JS: 2022-09-04 18:07:00 [error X 42][../wp-includes/js/jquery/jquery.min.js?ver=3.6.0:2:31703] Cannot read properties of undefined (reading \'tools\') 
JS: 2022-09-04 18:30:02 [error X 1][../wp-content/plugins/elementor/assets/js/frontend.min.js?ver=3.7.4:2:2235] Cannot read properties of undefined (reading \'data\') 
@MarleyPlant MarleyPlant added the status/awaiting_triage Indicates when an Issue, Pull Request, or Discussion awaits to be triaged. label Sep 5, 2022
@rami-elementor rami-elementor added type/developer-api Indicates when a topic is related to the Developer API and Documentation. and removed status/awaiting_triage Indicates when an Issue, Pull Request, or Discussion awaits to be triaged. labels Sep 6, 2022
@rami-elementor
Copy link
Contributor

Hi @MarleyPlant

Can you share your code here?

@MarleyPlant
Copy link
Author

MarleyPlant commented Sep 6, 2022

@rami-elementor

Handler.js

(function ($) {
  class ImageSliderHandler extends elementorModules.frontend.handlers.Base {
    currentSlide = 0;
    dotActiveClass = "image-slider__dot--active";

    getDefaultSettings() {
      return {
        selectors: {
          prevButton: ".image-slider__controls__prev",
          nextButton: ".image-slider__controls__next",
          dots: ".image-slider__dot",
          slides: ".image-slider__img",
        },
      };
    }

    getDefaultElements() {
      const selectors = this.getSettings("selectors");
      return {
        $nextButton: this.$element.find(selectors.nextButton),
        $prevButton: this.$element.find(selectors.prevButton),
        $slides: this.$element.find(selectors.slides),
        $dots: this.$element.find(selectors.dots),
      };
    }

    bindEvents() {
      this.elements.$nextButton.on("click", this.nextSlide.bind(this));
      this.elements.$prevButton.on("click", this.prevSlide.bind(this));
      this.elements.$dots.on("click", this.onDotClick.bind(this));
    }

    nextSlide() {
      this.resetSlides();
      this.currentSlide++;
      if (this.currentSlide > slides.length) {
        this.currentSlide = 1;
      }

      dots[this.currentSlide - 1].classList.add(this.dotActiveClass);
      this.showSlide(this.currentSlide - 1);
    }

    showSlide() {
      this.elememts.$slides[slide].style.display = "block";
    }

    resetSlides() {
      this.elements.$dots.removeClass(this.dotActiveClass);
      this.elements.$slides.css("display", "none");
    }

    prevSlide() {
      this.resetSlides();
      if (this.currentSlide > 0) {
        this.currentSlide--;
      } else if (this.currentSlide == 0) {
        this.currentSlide = this.elements.$slides.length - 1;
      }

      if (this.currentSlide > 0) {
        this.elements.$dots[this.currentSlide - 1].classList.add(
          this.dotActiveClass
        );
      } else {
        this.elements.$dots[slides.length - 1].classList.add(
          this.dotActiveClass
        );
      }
      this.showSlide(this.currentSlide);
    }

    onDotClick(event) {
      this.resetSlides();
      this.currentSlide = $(event.target).index();
      this.showSlide(this.currentSlide);
      event.target.classList.add(this.dotActiveClass);
    }
  }

  $(window).on("elementor/frontend/init", () => {
    elementorFrontend.elementsHandler.attachHandler(
      "image-slider",
      ImageSliderHandler
    );
  });
  
})(jQuery);

Widget.php

<?php

/**
 * Marleyplant Widgets Library
 * php version 7.3.1
 * 
 * @category   Scripts
 * @package    MarleyPlant
 * @subpackage Scripts
 * @author     Marley Plant <marley@marleyplant.com>
 * @license    Apache http://www.apache.org/licenses/
 * @link       https://marleyplant.com
 * @since      1.0
 */

use Elementor\Widget_Base;
use Elementor\Controls_Manager;

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

/**
 * Elementor Image Comparison Widget
 *
 * This class defines the Image Slider Widget
 * 
 * @category   Scripts
 * @package    MarleyPlant
 * @subpackage Scripts
 * @author     Marley Plant <marley@marleyplant.com>
 * @license    Apache http://www.apache.org/licenses/
 * @link       https://marleyplant.com
 * @since      1.0
 */

class ImageSlider extends \Elementor\Widget_Base
{

    /**
     * Retrieve the widget name.
     *
     * @since 1.0.0
     *
     * @access public
     *
     * @return string Widget name.
     */
    public function get_name()
    {
        return 'image-slider';
    }

    /**
     * Retrieve the widget title.
     *
     * @since 1.0.0
     *
     * @access public
     *
     * @return string Widget title.
     */
    public function get_title()
    {
        return __('Image Slider', 'marleyplant_widgets');
    }

    /**
     * Retrieve the widget icon.
     *
     * @since 1.0.0
     *
     * @access public
     *
     * @return string Widget icon.
     */
    public function get_icon()
    {
        return 'eicon-slider-full-screen';
    }

    /**
     * Retrieve the list of categories the widget belongs to.
     *
     * Used to determine where to display the widget in the editor.
     *
     * Note that currently Elementor supports only one category.
     * When multiple categories passed, Elementor uses the first one.   
     *
     * @since 1.0.0
     *
     * @access public
     *
     * @return array Widget categories.
     */
    public function get_categories()
    {
        return ['general'];
    }

    /**
     * Get custom help URL.
     *
     * Retrieve a URL where the user can get more information about the widget.
     *
     * @since 1.0.0
     * @access public
     * @return string Widget help URL.
     */
    public function get_custom_help_url()
    {
        return 'https://developers.elementor.com/docs/widgets/';
    }


    /**
     * Get widget keywords.
     *
     * Retrieve the list of keywords the oEmbed widget belongs to.
     *
     * @since 1.0.0
     * @access public
     * @return array Widget keywords.
     */
    public function get_keywords()
    {
        return ['plantek'];
    }


    /**
     * Retrieve the list of scripts the widget depended on.
     *
     * Used to set scripts dependencies required to run the widget.
     *
     * @since 1.0.0
     *
     * @access public
     *
     * @return array Widget scripts dependencies.
     */
    public function get_script_depends()
    {
        return ['image-slider-js'];
    }

    /**
     * Register the widget controls.
     *
     * Adds different input fields to allow the user to change and customize the widget settings.
     *
     * @since 1.0.0
     *
     * @access protected
     * @return null
     */
    protected function register_controls()
    {

        $this->start_controls_section(
            'content_section',
            [
                'label' => __('Content', 'plugin-name'),
                'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
            ]
        );

        $repeater = new \Elementor\Repeater();

        $repeater->add_control(
            'heading',
            [
                'label' => esc_html__('Title', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::TEXT,
                'default' => esc_html__('Title', 'plugin-name'),
                'label_block' => true,
            ]
        );

        $repeater->add_control(
            'image',
            [
                'label' => esc_html__('Choose Image', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::MEDIA,
                'default' => [
                    'url' => \Elementor\Utils::get_placeholder_image_src(),
                ],
            ]
        );

        $this->add_control(
            'slides',
            [
                'label' => esc_html__('Slides List', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::REPEATER,
                'fields' => $repeater->get_controls()
            ]
        );

        $this->end_controls_section();

        $this->start_controls_section(
            'section_style',
            [
                'label' => __('Style', 'marleyplant_widgets'),
                'tab' => Controls_Manager::TAB_STYLE,
            ]
        );

        $this->add_control(
            'color_slider',
            [
                'label' => esc_html__('Primary Color', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::COLOR,
                'selectors' => [
                    '{{WRAPPER}} .title' => 'color: {{VALUE}}',
                ],
            ]
        );

        $this->add_control(
            'sliderWidth',
            [
                'label' => __('Width', 'plugin-domain'),
                'type' => Controls_Manager::SLIDER,
                'size_units' => ['%', 'em', 'px'],
                'range' => [
                    '%' => [
                        'min' => 0,
                        'max' => 100,
                    ],
                ],
                'default' => [
                    'unit' => '%',
                    'size' => 100,
                ],
                'selectors' => [
                    '{{WRAPPER}} .image-slider' => 'width: {{SIZE}}{{UNIT}};',
                ],
            ]
        );

        $this->add_control(
            'sliderHeight',
            [
                'label' => __('Height', 'plugin-domain'),
                'type' => Controls_Manager::SLIDER,
                'size_units' => ['%', 'em', 'px'],
                'range' => [
                    '%' => [
                        'min' => 0,
                        'max' => 100,
                    ],
                    "em" => [
                        'min' => 0,
                        'max' => 100
                    ]
                ],
                'default' => [
                    'unit' => '%',
                    'size' => 100,
                ],
                'selectors' => [
                    '{{WRAPPER}} .image-slider' => 'height: {{SIZE}}{{UNIT}};',
                    '{{WRAPPER}} .image-slider__img' => 'height: {{SIZE}}{{UNIT}};',
                ],
            ]
        );

        $this->add_control(
            'objectFit',
            [
                'label' => esc_html__('Object Fit', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::SELECT,
                'default' => 'cover',
                'options' => [
                    'cover'  => esc_html__('Cover', 'plugin-name'),
                    'fill' => esc_html__('Fill', 'plugin-name'),
                    'contain' => esc_html__('Contain', 'plugin-name'),
                    'scale-down' => esc_html__('Scale Down', 'plugin-name'),
                ],
            ]
        );

        $this->add_control(
            'show_controls',
            [
                'label' => esc_html__('Show Controls?', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::SWITCHER,
                'label_on' => esc_html__('Show', 'your-plugin'),
                'label_off' => esc_html__('Hide', 'your-plugin'),
                'return_value' => 'yes',
                'default' => 'yes',
            ]
        );

        $this->add_control(
            'show_dots',
            [
                'label' => esc_html__('Show Dots?', 'plugin-name'),
                'type' => \Elementor\Controls_Manager::SWITCHER,
                'label_on' => esc_html__('Show', 'your-plugin'),
                'label_off' => esc_html__('Hide', 'your-plugin'),
                'return_value' => 'yes',
                'default' => 'yes',
            ]
        );

        $this->end_controls_section();
    }

    /**
     * Render the widget output on the frontend.
     *
     * Written in PHP and used to generate the final HTML.
     *
     * @since 1.0.0
     *
     * @access protected
     * @return null
     */
    protected function render()
    {
        include __DIR__ . '/template.php';
    }
}

@MarleyPlant
Copy link
Author

Would love to know the reason why this isn't working.

@mapsmarketing
Copy link

mapsmarketing commented Oct 12, 2022

Hi @MarleyPlant

Sounds like a similar problem I had when trying to use the SwiperBase.

At the bottom of the page you'll notice that the code needs to be transpiled:

For wider browser support, it is recommended to transpile your class into ES5 syntax (using a tool such as Babel.js) before deploying it in production.

However, you can create the Base class in another way as shown by meceware

My issue/post has info on how to transpile it if you want to do it that way: #19858

@Reichbaum
Copy link

I have the same problem. This bug only appears on the Twenty Twenty-Two and Twenty Twenty-Three themes (WP 6.1, Elementor 3.8.0, clean themes, no other plugins).

The console error:

jquery.min.js?ver=3.6.1:2 Uncaught TypeError: Cannot read properties of undefined (reading 'tools')
    at Frontend.initOnReadyComponents (frontend.min.js?ver=3.8.0:2:6682)
    at Frontend.init (frontend.min.js?ver=3.8.0:2:8809)
    at HTMLDocument.<anonymous> (frontend.min.js?ver=3.8.0:2:9035)
    at e (jquery.min.js?ver=3.6.1:2:30038)
    at t (jquery.min.js?ver=3.6.1:2:30340)

My code:

class WPCC_Widget extends Widget_Base {

	public function __construct( $data = [], $args = null ) {
	parent::__construct( $data, $args );

		if ( defined( 'WPCC_VERSION' ) ) {
			$version = WPCC_VERSION;
		} else {
			$version = '1.0.0';
		}

		wp_register_script( 'wpcc-elementor-script', plugin_dir_url( __FILE__ ) . 'assets/wpcc-elementor-script.js', array( 'elementor-frontend' ), $version, true );
	}
	
	public function get_script_depends() {
		return array( 'wpcc-elementor-script' );
	}
}

My Script:

(function ($) {
	class wpccClass extends elementorModules.frontend.handlers.Base {
		getDefaultSettings() {
			return {
				selectors: {
					widget: ".wpcc-elementor-block",
				},
			};
		}

		getDefaultElements() {
			const selectors = this.getSettings("selectors");

			return {
				$widgets: this.findElement(selectors.widget),
			};
		}

		bindEvents() {}

		onInit(...args) {
			console.log('here');
		}
	}

	$(window).on("elementor/frontend/init", () => {
		elementorFrontend.elementsHandler.attachHandler('wpcc-widget', wpccClass);
	});
})(jQuery);

Moreover, here in the documentation - https://developers.elementor.com/docs/scripts-styles/widget-scripts/ - there is no 'elementor-frontend' in the dependencies, but then the error is like this:

Uncaught ReferenceError: elementorModules is not defined
    at wpcc-elementor-script.js:2:36
    at wpcc-elementor-script.js:2:27
    at wpcc-elementor-script.js:1:1

But here - https://developers.elementor.com/add-javascript-to-elementor-widgets/#Registering_the_Widget_Handler_with_Elementor - 'elementor-frontend' is in the dependencies - and this solution works in all but these two themes.

I would appreciate any help!

@nicholaszein nicholaszein changed the title elementorModules.frontend.tools is undefined when creating a JS Handler for a Custom widget [馃 Evaluating] 馃悶 Bug Report: elementorModules.frontend.tools is undefined when creating a JS Handler for a Custom widget Mar 14, 2023
@nicholaszein nicholaszein added bug Indicates a bug with one or multiple components. compatibility/assets Indicates a compatibility problem with scripts or CSS. labels Mar 14, 2023
@nicoblg
Copy link

nicoblg commented Mar 21, 2023

Same problem here since a year i think. I noticed that this happens for logged in users, however i think i found a temporary workaround for this as the error reported by this bug report shows up even if you enqueue a blank js and this let me think that maybe is a priority issue.

It seems that the scripts are loaded before 'elementor-frontend' is actually loaded, so my solution is to add an high priority to the reccomended code as showed below:
add_action( 'wp_enqueue_scripts', 'your-register-function', 9999 )

This seems the only thing that works for now.

@YahyaTec
Copy link

YahyaTec commented Apr 5, 2023

Yes, this issue occurs till latest update of elementor

@Reichbaum
Copy link

I found this - the problem occurs on block themes:
https://ibb.co/pJ3TV4s

@POBrien333
Copy link

This also occurs with the MasterStudy LMS theme.

@Reichbaum
Copy link

Guys, this bug is still relevant - my custom widget is still causing this error on block themes. Any progress in this area?

@nicoblg
Copy link

nicoblg commented Jul 28, 2023

Guys, this bug is still relevant - my custom widget is still causing this error on block themes. Any progress in this area?

Hi, i think at this point that they did not want to solve this bug. As a solution, created a function in wich i register and localize all my scripts and then enqueue them on init.

So i have a function like this:

public function register_dependencies() {
    $dbp_js_ver  = date("ymd-Gis", filemtime( brainless_elements()->plugin_path . '/assets/js/db-post.js' ));
    wp_register_script( 'db-post-scripts', brainless_elements()->assets_url .'/js/db-post.js', [ 'elementor-frontend' ], $dbp_js_ver, true );
    wp_localize_script( 'db-post-scripts', 'db_post_ajax', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'check_nonce' => wp_create_nonce('db-nonce')) );
}

and then in init i do this:

add_action( 'wp_enqueue_scripts', [ $this, 'register_dependencies' ], 9999 );

shortly after registering my widgets and controls. I also noticed that elementor do not load all my registered files, but only the ones the widget depends on where the widget is loaded, using the

public function get_script_depends() {
    return [ 'db-post-scripts' ];
}

Hope it helps 馃

@Reichbaum
Copy link

Hi @nicoblg!

This solution continues to cause an error on block themes. I enqueued the script in the high priority way you suggested above and it works on block themes, but some users are having the problem when using some caching plugins. I'm not 100% sure this is where the problem lies as I haven't had access to the problem sites, but theoretically the possibility is there. Ok, too bad we can't influence this.....

@nicholaszein nicholaszein changed the title [馃 Evaluating] 馃悶 Bug Report: elementorModules.frontend.tools is undefined when creating a JS Handler for a Custom widget 馃悶 elementorModules.frontend.tools is undefined when creating a JS Handler for a Custom widget Jan 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates a bug with one or multiple components. compatibility/assets Indicates a compatibility problem with scripts or CSS. mod/e* type/developer-api Indicates when a topic is related to the Developer API and Documentation.
Projects
None yet
Development

No branches or pull requests

8 participants