• I developed a custom widget for the Elementor plugin. This plugin list the posts of a custom post type with an horizontal carousel created by a library ‘tiny-slider’, and this carousel have option to filter the posts by categories, date or tags. A click on one of this filter make an ajax request to update the query.

    The global structure of the site is a Bedrock architecture, with a custom theme use Sage 10. The render of my widget is handled by a Blade template and controller, and the ajax call request is made by Javascript file. The ajax control is handled by a Trait in another plugin.

    There is my actual structure and files :

    web
    ├─ app
    │  ├─ mu-plugins
    │  │  ├─ elementor-widgets
    │  │  │  ├─ widgets
    │  │  │  │  ├─ MyWidget
    │  │  │  │  │  ├─ js
    │  │  │  │  │  │  ├─ my-widget.js
    │  │  │  │  │  ├─ MyWidget.php
    │  │  │  ├─ elementor-widgets.php
    ├─ themes
    │  ├─ my-theme
    │  │  ├─ app
    │  │  │  ├─ View
    │  │  │  │  ├─ Composers
    │  │  │  │  │  ├─ Widget.php
    │  │  ├─ resources
    │  │  │  ├─ views
    │  │  │  │  ├─ widgets
    │  │  │  │  │  ├─ widget-view.blade.php

    elementor-widgets.php : Plugin for register my custom widgets

    function elementor_register_new_widgets($widgets_manager): void
    {
        require_once(__DIR__ . '/widgets/MyWidget/MyWidget.php');
    
        $widgets_manager->register(new MyWidget());
    }
    
    add_action('elementor/widgets/register', 'elementor_register_new_widgets');
    
    function elementor_add_widget_categories($elements_manager): void
    {
        $elements_manager->add_category(
            'custom',
            [
                'title' => 'Custom',
                'icon'  => 'fa fa-plug',
            ]
        );
    }
    
    add_action('elementor/elements/categories_registered', 'elementor_add_widget_categories');

    MyWidget.php : the custom widget declaration.

    use App\View\Composers\News;
    use Elementor\Widget_Base;
    
    class ElementorNewsCarousel extends Widget_Base
    {
        public function __construct($data = [], $args = null)
        {
            parent::__construct($data, $args);
    
            wp_register_script('my-widget', plugins_url('/js/my-widget.js', __FILE__), ['jquery', 'tiny-slider'], false, true);
        }
    
        // ... some code
    
        public function get_script_depends(): array
        {
            return ['my-widget']; // I add my custom JS declared in the constructor
        }
    
        // Render the widget view using the Sage theme
        protected function render(): void
        {
            $templateWidget = new Widget(); // the Widget class in the Composers folder
    
            echo view('widgets/widget-view', [
                'wp_query'          => $templateWidget->getNews(),
                'filter_categories' => $templateWidget->getFilterCategories(),
                'filter_tags'       => $templateWidget->getFilterTags(),
                'filter_date'       => $templateWidget->getFilterDate(),
                'chips_buttons'     => $templateWidget->getChipsButtons()
            ])->render();
        }
    }

    my-widget.js : the JS code use to interact with the widget.

    import { tns } from 'tiny-slider';
    
    jQuery(window).on('elementor/frontend/init', () => {
      const $ = jQuery;
    
      // Common
      const body         = $('body');
      const loadingModal = $('#modal-loading');
    
      // Homepage
      let homeCarouselNews            = $('#home-carousel-news');
      const homeFilterCategoriesBtn   = $('#news-filter-categories-btn');
      const homeFilterCategories      = $('#news-filter-category');
      const homeFilterCategoriesItems = $('.news-filter-category-list-item');
      const homeFilterTagsBtn         = $('#news-filter-tags-btn');
      const homeFilterTags            = $('#news-filter-tag');
      const homeFilterTagsItems       = $('.news-filter-tag-list-item');
      const homeFilterDateBtn   = $('#news-filter-date-btn');
      const homeFilterDate      = $('#news-filter-date');
      const homeFilterDateItems = $('.news-filter-date-list-item');
    
      let homeFilterCategoriesList = [];
      let homeFilterTagsList       = [];
      let homeFilterDateList       = [];
    
      let sliderHome = tns({
        container: '#home-carousel-news',
        items: 4,
        gutter: 36,
        fixedWidth: 280,
        slideBy: 1,
        controls: true,
        prevButton: '#home-carousel-controls-prev',
        nextButton: '#home-carousel-controls-next',
        nav: false,
        autoplay: false,
        loop: false,
        touch: true,
        freezable: true,
      });
    
      const showOrHideFilterPopup = function (event) {
        const popup = event.data.element;
        popup.toggleClass('hidden');
      }
    
      const hideAllPopUp = function () {
        $(homeFilterCategories).addClass('hidden');
        $(homeFilterTags).addClass('hidden');
        $(homeFilterDate).addClass('hidden');
      }
    
      const updateHomeFilters = function (categoriesList, tagsList, dateList) {
        hideAllPopUp();
    
        const getParamsStr = window.location.search ? window.location.search : '?'
    
        $.ajax({
          url: '/wp/wp-admin/admin-ajax.php' + getParamsStr + '&paged=1',
          type: 'POST',
          data: {
            'action': 'news_filter_homepage',
            'categoriesList': categoriesList.toString(),
            'tagsList': tagsList.toString(),
            'dateList': dateList.toString(),
          },
        }).done(function (response) {
          
          sliderHome.destroy();
          homeCarouselNews = $('#home-carousel-news');
          homeCarouselNews.empty();
          homeCarouselNews.append(response);
          sliderHome = sliderHome.rebuild();
    
        }).fail(function (response) {
          console.error('updateHomeFilters', 'fail', response);
          hideLoadingModal();
        });
      }
    
      const updateElementFilter = function (event) {
        if (!(event.target.tagName === 'LABEL')) {
          const elementId = $(event.data.element).find('input').val();
          const elementList = event.data.list;
          const elementType = event.data.type;
    
          if (!elementList.includes(elementId)) {
            elementList.push(elementId)
          } else {
            const index = elementList.indexOf(elementId);
            elementList.splice(index, 1);
          }
    
          updateHomeFilters(homeFilterCategoriesList, homeFilterTagsList, homeFilterDateList);
        }
      }
    
      //// Categories
      if (homeFilterCategoriesBtn) {
        homeFilterCategoriesBtn.click({
          element: homeFilterCategories,
          button: homeFilterCategoriesBtn,
        }, showOrHideFilterPopup)
      }
    
      if (homeFilterCategoriesItems) {
        homeFilterCategoriesItems.each(function () {
          $(this).click({ element: $(this), list: homeFilterCategoriesList, type: 'category' }, updateElementFilter);
        })
      }
    
      //// Tags
      if (homeFilterTagsBtn) {
        homeFilterTagsBtn.click({
          element: homeFilterTags,
          button: homeFilterTagsBtn,
        }, showOrHideFilterPopup)
      }
    
      if (homeFilterTagsItems) {
        homeFilterTagsItems.each(function () {
          $(this).click({ element: $(this), list: homeFilterTagsList, type: 'tag' }, updateElementFilter);
        })
      }
    
      //// Date
      if (homeFilterDateBtn) {
        homeFilterDateBtn.click({
          element: homeFilterDate,
          button: homeFilterDateBtn,
        }, showOrHideFilterPopup)
      }
    
      if (homeFilterDateItems) {
        homeFilterDateItems.each(function () {
          $(this).click({ element: $(this), list: homeFilterDateList, type: 'date' }, updateElementFilter);
        })
      }
    });

    My code works if I put the JS file in the theme’s folder assets, but the JS file is loaded in all pages. What I want is, like the Elementor’s documentation say, insert the JS directly inside the widget, to only load the JS when the widget is on the page. But with the architecture I wrote here, my JS file seems to be not loaded by the page. My carousel and ajax calls are not working. But, all of the view is correctly rendered. Any idea of why my JS file is not loaded by the widget?

    • This topic was modified 1 year, 10 months ago by Auraylien. Reason: typos
Viewing 2 replies - 1 through 2 (of 2 total)
Viewing 2 replies - 1 through 2 (of 2 total)
  • The topic ‘Use custom Javascript for a custom Elementor Widget’ is closed to new replies.