Creating FAQ extension for elementor from scratch

Elementor created a strong presence in the WordPress ecosystem. Millions of websites are created with the Elementor page builder. From my experience, it gives the ultimate power to create any layout amongst the existing page builders. To enhance its power, we might need to develop some addons that make our lives easier because your client may need something that does not exist in the Elementor. However, it is pretty easy to create your addon.

In this article, I am going to demonstrate how you can create an Elementor addon. We will build an FAQ addon where you can add FAQs as much as you want using the repeater field, and can change the title colour, and title background colour but you can add more control or even create anything if you get the idea.

Folder structure

Let’s name our plugin folder elementor-faq. So inside this folder, there are two more folders called assets and widgets and one file elementor-faq.php. assets folder contains our css and js files, widgets folder contains ttd-faq.php.

Now lets describe each of the files one by one.

Main plugin files

elementor-faq.php contains the code below.

<?php
/**
* Plugin Name: Elementor FAQ addon
* Description: Simple FAQ addon for Elementor.
* Version: 1.0.0
* Author: The Techy Dots
* Author URI: https://thetechydots.com/
* Text Domain: ttd-faq
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
define( 'TTD_FAQ_URL', plugins_url( '/', __FILE__ ) );
function register_ttd_faq_widget( $widgets_manager ) {
require_once( __DIR__ . '/widgets/ttd_faq.php' );
$widgets_manager->register( new \Elementor_Ttd_Faq_Widget() );
}
add_action( 'elementor/widgets/register', 'register_ttd_faq_widget' );

In the first few commented lines we declared plugin header fields. We name our plugin Elementor FAQ addon. Then added plugin description, version, author name, author uri and text domain.

First few lines of php code are the most basic requirements for a plugin. Then we registered register_ttd_faq_widget function with elementor/widgets/register action hook. elementor/widgets/register hook tells the WordPress; hey, a new widget is coming.

ttd_faq.php file contains all important methods for our addon.

Note: A method and a function are same thing. When a function is declared inside a class, it is called method otherwise function.

<?php
class Elementor_Ttd_Faq_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'ttq_faq';
}
public function get_title() {
return esc_html__( 'TTD FAQ', 'ttd-faq' );
}
public function get_icon() {
return 'eicon-toggle';
}
public function get_categories() {
return [ 'basic' ];
}
public function get_keywords() {
return [ 'faq', 'list' ];
}
public function __construct( $data=[], $args=null ){
parent::__construct( $data, $args );
wp_register_style( 'ttd-faq-css', TTD_FAQ_URL . 'assets/css/ttd-faq-style.css', '', time() );
wp_register_script('ttd-faq-js', TTD_FAQ_URL . 'assets/js/ttd-faq-script.js', ['elementor-frontend'], time(), true );
}
public function get_style_depends() {
return [ 'ttd-faq-css' ];
}
public function get_script_depends() {
return [ 'ttd-faq-js' ];
}
protected function register_controls() {
// Content Tab Start
$this->start_controls_section(
'content_section',
[
'label' => esc_html__( 'Content', 'ttd-faq' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$repeater = new \Elementor\Repeater();
$repeater->add_control(
'list_title', [
'label' => esc_html__( 'Title', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'List Title' , 'ttd-faq' ),
'label_block' => true,
]
);
$repeater->add_control(
'list_content', [
'label' => esc_html__( 'Content', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::WYSIWYG,
'default' => esc_html__( 'List Content' , 'ttd-faq' ),
'show_label' => false,
]
);
$repeater->add_control(
'list_color',
[
'label' => esc_html__( 'Color', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} {{CURRENT_ITEM}}' => 'color: {{VALUE}}'
],
]
);
$this->add_control(
'list',
[
'label' => esc_html__( 'FAQ List', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::REPEATER,
'fields' => $repeater->get_controls(),
'default' => [
[
'list_title' => esc_html__( 'FAQ #1', 'ttd-faq' ),
'list_content' => esc_html__( 'Item content. Click the edit button to change this text.', 'ttd-faq' ),
],
[
'list_title' => esc_html__( 'FAQ #2', 'ttd-faq' ),
'list_content' => esc_html__( 'Item content. Click the edit button to change this text.', 'ttd-faq' ),
],
],
'title_field' => '{{{ list_title }}}',
]
);
$this->end_controls_section();
// Content Tab End
// Style Tab Start
$this->start_controls_section(
'section_title_style',
[
'label' => esc_html__( 'Title', 'ttd-faq' ),
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'title_color',
[
'label' => esc_html__( 'Title Color', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .accordion-title' => 'color: {{VALUE}};',
],
]
);
$this->add_control(
'title_background_color',
[
'label' => esc_html__( 'Title Background Color', 'ttd-faq' ),
'type' => \Elementor\Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .accordion-title' => 'background-color: {{VALUE}};',
],
]
);
$this->end_controls_section();
// Style Tab End
}
protected function render() {
$settings = $this->get_settings_for_display();
if ( $settings['list'] ) {
echo '<div class="accordion">';
foreach ( $settings['list'] as $item ) {
echo '<div class="accordion-single acc-close">';
echo '<div class="accordion-title accordion-title-' . esc_attr( $item['_id'] ) . '">' . $item['list_title'] . '<span class="plus">+</span></div>';
echo '<div class="accordion-content">' . $item['list_content'] . '</div>';
echo '</div>';
}
echo '</div>';
}
}
protected function content_template() {
?>
<# if ( settings.list.length ) { #>
<div class="accordion">
<# _.each( settings.list, function( item ) { #>
<div class="accordion-single acc-close">
<div class="accordion-title accordion-title-{{ item._id }}">{{{ item.list_title }}}<span class="plus">+</span>
</div>
<div class="accordion-content">{{{ item.list_content }}}</div>
</div>
<# }); #>
</div>
<# } #>
<?php
}
}

Let’s explain each methods.

get_name:  assigns the slug for the widget

get_title: assigns the title of the widget

Addon title

Addon title

get_icon: assigns the icon of the widget

Addon icon

get_categories: assigns the widget in a category

Elementor addon category

get_keywords: assigns keywords to the widget so that we can find that widget by typing those keywords in the search box.

Elementor addon keywords

__construct, get_style_depends, get_script_depends: In __construct method we registered our necessary css and js files. In get_style_depends we enqueued the css files and get_script_depends used for enqueuing the js files. It is the same way how WordPress enqueues styles and scripts but the Elementor way. I described here in details how enqueuing works in WordPress

Enqueue scripts in Elementor

Widget controls

Next method is register_controls. Inside this method we should add all of the widget controls. A set of controls go to “Content” tab and another set of controls belongs to “style” tab. Each set starts with start_controls_section statement and ends with end_controls_section.
add_control is used to add input, textarea, wysiwyg, radio fields etc. You can learn more about controls in Elementor’s official documentation.

Displaying in the frontend

render() method is responsible for displaying the frontend stuffs in the frontend (i.e FAQ title, description, colour etc.)

First we initialized a variable named $settings where we stored all settings. We run a foreach loop to get the FAQ titles, descriptions.

Live preview in the editor

content_template() method is written in JS. It has everything we did in the render() method. It helps to render contents in live preview at the right hand side in Elementor editor.

Adding CSS and JS code

Let’s add the CSS code to design FAQs.

.accordion-single{
margin: 0px 0px 10px 0px;
}
.accordion-title span{
float: right;
}
.acc-close .accordion-content{
height:0px;
transition: transform 0.4s ease;
transform: scaleY(0);
display:block;
}
.acc-open .accordion-content{
padding: 20px;
background-color: #f0f1f1;
border: 1px solid #ddd;
width: 100%;
margin: 0px 0px 10px 0px;
display:block;
transform: scaleY(1);
transform-origin: top;
transition: transform 0.4s ease;
box-sizing: border-box;
}
.acc-open .accordion-title, .accordion-title{
margin:0px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom-right-radius: 0px;
border-bottom-left-radius: 0px;
background-color: #ddd;
border: 1px solid #ddd;
padding: 5px 20px;
}

It’s time to add some JS code

var accSingle = document.getElementsByClassName('accordion-single');
var accTitle = document.getElementsByClassName('accordion-title');
for (i = 0; i < accTitle.length; i++) {
accTitle[i].addEventListener('click', toggleItem, false);
}
function toggleItem() {
var itemClass = this.parentNode.className;
for (i = 0; i < accSingle.length; i++) {
accSingle[i].className = 'accordion-single acc-close';
}
if (itemClass == 'accordion-single acc-close') {
this.parentNode.className = 'accordion-single acc-open';
}
}

You can change the css and js code as you want according to your need.

We will be happy to hear your thoughts

Leave a reply

The Techy Dots
Logo
Compare items
  • Total (0)
Compare
0