Das Problem / Der Bedarf

Wenn Sie bereits eine Elementor-Website ausgeliefert haben und ein Kunde fragt: „Ich möchte ein Widget“, Mass angefertigt„aber ohne 12 Add-ons zu installieren“, haben Sie bereits den richtigen Reflex: Gehen Sie den Code durch.

Elementor (Free/Pro) bietet eine ausreichend stabile API mit Hooks und Events, um den Builder optimal anzupassen: Kategorien von Widgets hinzufügen, benutzerdefinierte Widgets speichern, Steuerelemente einfügen, Standardwerte erzwingen, Skripte nur bei Bedarf laden und sogar dynamische Felder über ein Tag hinzufügen.

Der typische Geschäftsbedarf: wiederverwendbare Komponenten (CTAs, Autorenboxen, Produktblätter, Tabellen, DSGVO-Banner usw.) sollen industrialisiert werden, ohne die Benutzerfreundlichkeit von Elementor zu beeinträchtigen. Am Ende dieses Kurses wissen Sie, wie Sie ein sauberes, kompatibles Mini-Plugin erstellen. WordPress Mit 6.9.4+ und PHP 8.1+ erhalten Sie eine wiederverwendbare Basis für Ihre Projekte.

Kurze Zusammenfassung

  • Erstelle ein Mini-Plugin WordPress (kein austauschbarer Code-Schnipsel), der sich in Elementor integrieren lässt, ohne die Funktionalität zu beeinträchtigen.Admin.
  • Verwenden Sie die korrekten Elementor-Hooks: elementor/init, elementor/widgets/register, elementor/elements/categories_registered, elementor/frontend/after_register_scripts.
  • Fügen Sie ein benutzerdefiniertes „Badge“-Widget (Titel + Text + Farbe + Symbol) mit Steuerelementen, sicherer Darstellung und Stilen hinzu.
  • Fügen Sie ein dynamisches Tag hinzu (erweiterte Option), um einen Wert aus den Benutzermetadaten (z. B. Position/Rolle) einzufügen.
  • CSS/JS wird nur geladen, wenn ein Widget aus Ihrem Plugin vorhanden ist (vermeidet "das Laden von allem überall").

Wann sollte diese Lösung verwendet werden?

  • Sie wünschen sich eine stabile, versionierte Komponente, die auf mehreren Plattformen (Agentur, Freelancer, Team) wiederverwendet werden kann.
  • Sie müssen sich an einen Styleguide (Farben, Typografie, Abstände) halten, ohne dem Kunden 50 „gefährliche“ Optionen zu überlassen.
  • Sie benötigen eine präzise Front-End-Darstellung, ohne auf ein Drittanbieter-Add-on angewiesen zu sein, das sich ohne Vorwarnung ändern kann.
  • Sie möchten die Performance verbessern: Assets werden nur bei Bedarf geladen, keine „großen Widget-Pakete“.
  • Sie möchten WordPress-Daten (Metadaten, Optionen, ACF/Pods usw.) über dynamische Tags integrieren.

Wann diese Lösung NICHT verwendet werden sollte

  • Der Bedarf ist rein visuell und gelegentlich: ein einfach Eine Elementor-Vorlage, ein Container und ein wenig CSS genügen möglicherweise.
  • Sie haben keine Kontrolle über die Wartung: Ein benutzerdefiniertes Widget erfordert die Einhaltung von Elementor (und manchmal auch dessen veralteten Funktionen).
  • Wenn Sie Elementor umfassend „patchen“ möchten (z. B. das interne Verhalten des Editors ändern), ist Stabilität selten gegeben. Greifen Sie lieber zu offiziellen Erweiterungen oder nehmen Sie einen gewissen Grad an technischer Schuld in Kauf.
  • Ihr Kunde verwendet hauptsächlich Gutenberg/Blocks: In diesem Fall ist ein benutzerdefinierter Block (Block-API) oft besser geeignet. Siehe die offizielle Dokumentation: Block-Editor-Handbuch.

Voraussetzungen / vor Beginn

  • WordPress 6.9.4+ und PHP 8.1+ (idealerweise 8.2/8.3 im Jahr 2026, falls Ihr Hosting-Anbieter dies unterstützen kann).
  • Elementor ist installiert und aktiviert (für dieses Widget-Beispiel reicht die kostenlose Version aus). Für erweiterte dynamische Tags wird häufig Elementor Pro verwendet, wir bevorzugen jedoch nach Möglichkeit öffentliche APIs.
  • Eine Testumgebung und ein Backup sind unerlässlich, bevor Änderungen vorgenommen werden. Ich habe schon oft erlebt, dass Code-Schnipsel in die Produktionsumgebung eingefügt wurden, was zu einem schwerwiegenden Fehler führte und die Administrationsoberfläche blockierte.
  • Ein Log-Plugin (oder zumindest WP_DEBUG_LOG) um PHP-Fehler zu lesen.

Nützliche WordPress-Referenzen:

Der naive Ansatz (und warum man ihn vermeiden sollte)

Der klassische Ansatz: einen großen Codeabschnitt einfügen in functions.php des Themes (oft ohne Child-Theme), überall Skripte speichern und Elementor-Klassen beim Laden instanziieren.

Typisches Beispiel (Anti-Pattern)

<?php
// ❌ Exemple volontairement mauvais : ne copiez pas tel quel.

add_action('init', function () {
    // ❌ Elementor n'est pas forcément chargé ici, et cette classe peut ne pas exister.
    $widgets_manager = ElementorPlugin::instance()->widgets_manager;

    require_once __DIR__ . '/widgets/badge.php';
    $widgets_manager->register(new My_Badge_Widget());

    // ❌ Charge CSS/JS sur toutes les pages, même si le widget n'est pas utilisé.
    wp_enqueue_style('my-badge', get_stylesheet_directory_uri() . '/badge.css');
});

Warum es (oft) kaputt geht

  • Timing Elementor hatte die Initialisierung seiner Manager zum Zeitpunkt der init (abhängig von Versionen/Kontexten).
  • fataler Fehler : wenn Elementor deaktiviert ist, ElementorPlugin ist nicht vorhanden.
  • Leistung : CSS/JS wird überall geladen, auch auf Seiten, die Elementor nicht verwenden.
  • Wartung : Im Theme verlorener Code, keine Möglichkeit zur korrekten Versionierung, anfällig beim Wechsel des Themes.

Die richtige Herangehensweise – eine Schritt-für-Schritt-Anleitung

Wir werden ein Mini-Plugin erstellen, mit Folgendem:

  • ein Bootstrap-Prozess, der prüft, ob Elementor aktiv ist.
  • eine eigene Kategorie von Widgets,
  • ein benutzerdefiniertes Widget
  • eine bedingte Anlagenbelastung,
  • eine optionale Variante namens „Dynamic Tag“ zur Veranschaulichung eines erweiterten Filters/Registers.

Schritt 1 – Plugin erstellen

Erstellen Sie diesen Ordner: wp-content/plugins/bpcab-elementor-hooks/

Dann diese Datei: wp-content/plugins/bpcab-elementor-hooks/bpcab-elementor-hooks.php

Schritt 2 – Bootstrap + Elementor-Verifizierung

Wir fügen unseren Code bei an plugins_loaded Dann warten wir. elementor/initDer entscheidende Punkt: Elementor-Klassen sollten erst aufgerufen werden, wenn das Plugin bereit ist.

Schritt 3 – Kategorie und Widget speichern

Elementor stellt dafür spezielle Aktionen bereit. In der Praxis haben sich diese seit mehreren Versionen als stabil erwiesen:

  • elementor/elements/categories_registered um eine Kategorie hinzuzufügen,
  • elementor/widgets/register ein Widget speichern.

Ich bestehe darauf: Vermeiden Sie die willkürliche Verwendung von Hooks (wie z. B. init ou wp_loaded) um Elementor zu berühren. Das Problem entsteht selten im Code des Widgets, sondern erst ab dem Zeitpunkt seiner Ausführung.

Schritt 4 – CSS/JS zum richtigen Zeitpunkt laden

Vermögenswerte werden erfasst über elementor/frontend/after_register_styles / elementor/frontend/after_register_scriptsdann wir enqueue nur wenn das Widget tatsächlich gerendert wird.

Schritt 5 – (Optional) Dynamisches Tag hinzufügen

Wenn Sie Elementor Pro verwenden (oder Ihre Architektur dynamische Tags unterstützt), ist ein benutzerdefinierter Tag oft übersichtlicher als ein Shortcode. Sie stellen die Daten bereit, und Elementor kümmert sich um das Einfügen in seine dynamischen Steuerelemente.

Vollständiger Code

Kopieren Sie einfach den unten stehenden Text. Das Plugin ist in sich abgeschlossen, und Sie können später Widgets hinzufügen.

Datei 1 — bpcab-elementor-hooks.php

<?php
/**
 * Plugin Name: BPCAB - Personnalisation Elementor par hooks
 * Description: Exemple pédagogique : catégorie + widget custom + assets conditionnels + (option) Dynamic Tag.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCAB
 * License: GPLv2 or later
 */

declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

final class BPCAB_Elementor_Hooks_Plugin {

	public const VERSION = '1.0.0';
	public const SLUG    = 'bpcab-elementor-hooks';

	private static ?self $instance = null;

	public static function instance(): self {
		if (null === self::$instance) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action('plugins_loaded', [$this, 'bootstrap']);
	}

	public function bootstrap(): void {
		// Elementor définit généralement ELEMENTOR_VERSION quand il est actif.
		if (!defined('ELEMENTOR_VERSION')) {
			// Pas d'Elementor : on ne fait rien. Évitez d'afficher une notice agressive en front.
			add_action('admin_notices', [$this, 'admin_notice_missing_elementor']);
			return;
		}

		// On attend l'initialisation d'Elementor avant d'appeler ses classes/managers.
		add_action('elementor/init', [$this, 'on_elementor_init']);
	}

	public function admin_notice_missing_elementor(): void {
		if (!current_user_can('activate_plugins')) {
			return;
		}

		$plugin_name = esc_html__('BPCAB - Personnalisation Elementor par hooks', 'bpcab');
		$message     = esc_html__('Elementor doit être activé pour utiliser ce plugin.', 'bpcab');

		echo '<div class="notice notice-warning">';
		echo '<p><strong>' . $plugin_name . '</strong> — ' . $message . '</p>';
		echo '</div>';
	}

	public function on_elementor_init(): void {
		// 1) Catégorie de widgets.
		add_action('elementor/elements/categories_registered', [$this, 'register_category']);

		// 2) Widgets.
		add_action('elementor/widgets/register', [$this, 'register_widgets']);

		// 3) Assets : on les enregistre au bon moment côté front.
		add_action('elementor/frontend/after_register_styles', [$this, 'register_frontend_styles']);
		add_action('elementor/frontend/after_register_scripts', [$this, 'register_frontend_scripts']);

		// 4) Option : Dynamic Tag (si l'API est disponible).
		add_action('elementor/dynamic_tags/register', [$this, 'register_dynamic_tags']);
	}

	public function register_category($elements_manager): void {
		// $elements_manager est typiquement une instance de ElementorElements_Manager.
		$elements_manager->add_category(
			'bpcab',
			[
				'title' => esc_html__('BPCAB', 'bpcab'),
				'icon'  => 'fa fa-plug',
			]
		);
	}

	public function register_widgets($widgets_manager): void {
		// Chargement des classes de widgets.
		require_once __DIR__ . '/includes/widgets/class-bpcab-widget-badge.php';

		// Enregistrement.
		$widgets_manager->register(new BPCAB_Widget_Badge());
	}

	public function register_frontend_styles(): void {
		wp_register_style(
			'bpcab-badge',
			plugins_url('assets/css/badge.css', __FILE__),
			[],
			self::VERSION
		);
	}

	public function register_frontend_scripts(): void {
		wp_register_script(
			'bpcab-badge',
			plugins_url('assets/js/badge.js', __FILE__),
			[],
			self::VERSION,
			true
		);
	}

	public function register_dynamic_tags($dynamic_tags_manager): void {
		// Certains sites n'utilisent pas cette feature : on protège le require.
		require_once __DIR__ . '/includes/dynamic-tags/class-bpcab-dynamic-tag-user-position.php';

		// Enregistrement du tag.
		$dynamic_tags_manager->register(new BPCAB_Dynamic_Tag_User_Position());
	}
}

BPCAB_Elementor_Hooks_Plugin::instance();

Datei 2 — includes/widgets/class-bpcab-widget-badge.php

<?php
declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

use ElementorWidget_Base;
use ElementorControls_Manager;
use ElementorIcons_Manager;

final class BPCAB_Widget_Badge extends Widget_Base {

	public function get_name(): string {
		return 'bpcab_badge';
	}

	public function get_title(): string {
		return esc_html__('Badge (BPCAB)', 'bpcab');
	}

	public function get_icon(): string {
		return 'eicon-badge';
	}

	public function get_categories(): array {
		return ['bpcab'];
	}

	public function get_keywords(): array {
		return ['badge', 'label', 'cta', 'bpcab'];
	}

	public function get_style_depends(): array {
		// Elementor enqueuera ce style seulement si le widget est présent.
		return ['bpcab-badge'];
	}

	public function get_script_depends(): array {
		// Idem pour le script.
		return ['bpcab-badge'];
	}

	protected function register_controls(): void {

		$this->start_controls_section(
			'section_content',
			[
				'label' => esc_html__('Contenu', 'bpcab'),
				'tab'   => Controls_Manager::TAB_CONTENT,
			]
		);

		$this->add_control(
			'title',
			[
				'label'       => esc_html__('Titre', 'bpcab'),
				'type'        => Controls_Manager::TEXT,
				'default'     => esc_html__('Nouveau', 'bpcab'),
				'placeholder' => esc_html__('Ex: Nouveau', 'bpcab'),
				'label_block' => true,
			]
		);

		$this->add_control(
			'text',
			[
				'label'       => esc_html__('Texte', 'bpcab'),
				'type'        => Controls_Manager::TEXTAREA,
				'default'     => esc_html__('Offre limitée', 'bpcab'),
				'placeholder' => esc_html__('Ex: Offre limitée', 'bpcab'),
				'rows'        => 3,
			]
		);

		$this->add_control(
			'icon',
			[
				'label'   => esc_html__('Icône', 'bpcab'),
				'type'    => Controls_Manager::ICONS,
				'default' => [
					'value'   => 'fas fa-star',
					'library' => 'fa-solid',
				],
			]
		);

		$this->end_controls_section();

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

		$this->add_control(
			'bg_color',
			[
				'label'     => esc_html__('Couleur de fond', 'bpcab'),
				'type'      => Controls_Manager::COLOR,
				'default'   => '#111827',
				'selectors' => [
					'{{WRAPPER}} .bpcab-badge' => 'background-color: {{VALUE}};',
				],
			]
		);

		$this->add_control(
			'text_color',
			[
				'label'     => esc_html__('Couleur du texte', 'bpcab'),
				'type'      => Controls_Manager::COLOR,
				'default'   => '#ffffff',
				'selectors' => [
					'{{WRAPPER}} .bpcab-badge' => 'color: {{VALUE}};',
				],
			]
		);

		$this->add_responsive_control(
			'padding',
			[
				'label'      => esc_html__('Padding', 'bpcab'),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => ['px', 'em', 'rem'],
				'default'    => [
					'top'    => 12,
					'right'  => 14,
					'bottom' => 12,
					'left'   => 14,
					'unit'   => 'px',
				],
				'selectors'  => [
					'{{WRAPPER}} .bpcab-badge' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				],
			]
		);

		$this->end_controls_section();
	}

	protected function render(): void {
		$settings = $this->get_settings_for_display();

		// Sanitization/escaping : Elementor stocke des valeurs, mais vous devez sortir du HTML propre.
		$title = isset($settings['title']) ? sanitize_text_field((string) $settings['title']) : '';
		$text  = isset($settings['text']) ? wp_kses_post((string) $settings['text']) : '';

		// Icône : Elementor fournit Icons_Manager pour rendre proprement.
		$icon = $settings['icon'] ?? null;

		echo '<div class="bpcab-badge" role="note">';
		echo '<div class="bpcab-badge__head">';

		if (!empty($icon) && is_array($icon)) {
			echo '<span class="bpcab-badge__icon" aria-hidden="true">';
			Icons_Manager::render_icon($icon, ['aria-hidden' => 'true']);
			echo '</span>';
		}

		if ($title !== '') {
			echo '<strong class="bpcab-badge__title">' . esc_html($title) . '</strong>';
		}

		echo '</div>';

		if ($text !== '') {
			// wp_kses_post permet un sous-ensemble HTML (liens, strong, em, etc.).
			echo '<div class="bpcab-badge__text">' . $text . '</div>';
		}

		echo '</div>';
	}
}

Datei 3 — includes/dynamic-tags/class-bpcab-dynamic-tag-user-position.php

<?php
declare(strict_types=1);

if (!defined('ABSPATH')) {
	exit;
}

use ElementorCoreDynamicTagsTag;

final class BPCAB_Dynamic_Tag_User_Position extends Tag {

	public function get_name(): string {
		return 'bpcab-user-position';
	}

	public function get_title(): string {
		return esc_html__('Utilisateur : Poste (BPCAB)', 'bpcab');
	}

	public function get_group(): string {
		// Groupe "Site" ou "User" selon votre organisation.
		return 'site';
	}

	public function get_categories(): array {
		// Catégorie TEXT pour insertion dans des champs texte.
		return [ElementorModulesDynamicTagsModule::TEXT_CATEGORY];
	}

	protected function register_controls(): void {
		// Exemple : choisir une meta key (simple). En prod, vous pourriez proposer une liste.
		$this->add_control(
			'meta_key',
			[
				'label'       => esc_html__('Meta key utilisateur', 'bpcab'),
				'type'        => ElementorControls_Manager::TEXT,
				'default'     => 'position',
				'placeholder' => 'position',
			]
		);
	}

	public function render(): void {
		$user_id = get_current_user_id();
		if (!$user_id) {
			return;
		}

		$settings = $this->get_settings();
		$meta_key = isset($settings['meta_key']) ? sanitize_key((string) $settings['meta_key']) : 'position';

		$value = get_user_meta($user_id, $meta_key, true);
		if (!is_scalar($value) || $value === '') {
			return;
		}

		echo esc_html((string) $value);
	}
}

Datei 4 — assets/css/badge.css

.bpcab-badge{
	display:block;
	border-radius:12px;
	line-height:1.35;
}
.bpcab-badge__head{
	display:flex;
	gap:10px;
	align-items:center;
}
.bpcab-badge__icon{
	display:inline-flex;
}
.bpcab-badge__title{
	font-weight:700;
}
.bpcab-badge__text{
	margin-top:8px;
	opacity:.95;
}

Datei 5 — assets/js/badge.js

(function () {
	// Script minimal : exemple de point d'accroche.
	// Ici, on ne fait rien de critique. Gardez vos widgets robustes sans JS si possible.
})();

Code-Erklärung

1) Warum ein Plugin (und nicht functions.php)?

Ein Plugin bietet einen transparenten Lebenszyklus, Aktivierung/Deaktivierung, Versionsverwaltung und einen stabilen Speicherort für Ihre Klassen. Ich habe oft erlebt, dass bei Aktualisierungen von Avada/Divi-Websites ein kleiner Teil des Themes verschwindet oder inkompatibel wird.

2) Der entscheidende Punkt: das Timing der Haken

  • plugins_loaded WordPress hat die Plugins geladen. Wir können überprüfen, ob Elementor vorhanden ist.
  • elementor/init Elementor hat seinen Hauptcontainer initialisiert. Hier fügen Sie Ihre Elementor-Hooks hinzu.
  • elementor/widgets/register Sie erhalten den Widget-Manager und speichern Ihre Klassen.
  • elementor/elements/categories_registered Sie deklarieren eine Kategorie, die in der Benutzeroberfläche des Builders sichtbar sein soll.

Diese Fehleranalyse vermeidet den klassischen Bug: „Klasse 'ElementorPlugin' nicht gefunden“ oder „Aufruf der Memberfunktion register() für null“.

3) Bedingte Belastung der Vermögenswerte

Das duo get_style_depends() / get_script_depends() wird zu selten genutzt. Dabei ist es eine der elegantesten Methoden, Ihre Assets erst dann zu laden, wenn Elementor Ihr Widget rendert.

Im Hintergrund: Elementor ermittelt die Abhängigkeiten der Widgets auf der Seite und fragt die entsprechenden Handles ab. Sie müssen lediglich... wp_register_style() / wp_register_script() Zur richtigen Zeit.

4) Sicheres Rendering: Bereinigung + Escape-Sequenzen

  • Entrée Elementor speichert Werte in der Datenbank. Diese müssen je nach Kontext dennoch bereinigt werden.
  • Ausgang : esc_html() für Text wp_kses_post() wenn Sie eingeschränktes HTML zulassen.

Die Falle, die ich am häufigsten sehe: direkt hinauszugehen $settings['text'] ohne wp_kses_post() „Weil es der Administrator ist.“ Auf einer Website mit mehreren Autoren stellt dies ein XSS-Risiko dar.

5) Dynamische Tags: Warum sie nützlich sind

Ein dynamisches Tag macht Shortcodes in Elementor-Feldern überflüssig. Sie stellen Daten (Benutzermetadaten, Optionen, ACF-Feld) bereit, und der Benutzer wählt das Tag in der Benutzeroberfläche aus. Dies ist deutlich wartungsfreundlicher als ein Shortcode, der in 30 Widgets eingefügt wird.

Varianten und Anwendungsfälle

Variante 1 – Erzwingen von „gesperrten“ Werten (weniger Kundenoptionen)

Wenn Sie verhindern möchten, dass der Kunde bestimmte Optionen ändert, können Sie Folgendes tun:

  • Die Steuerung nicht freilegen (nein add_control),
  • oder eine geschlossene Liste anzeigen (SELECT),
  • oder einen Wert festlegen render().

Beispiel: Zuweisung einer CSS-Klasse basierend auf einem „Typ“:

// Dans register_controls()
$this->add_control(
	'type',
	[
		'label'   => esc_html__('Type', 'bpcab'),
		'type'    => Controls_Manager::SELECT,
		'default' => 'info',
		'options' => [
			'info'    => esc_html__('Info', 'bpcab'),
			'warning' => esc_html__('Alerte', 'bpcab'),
		],
	]
);

// Dans render()
$type = isset($settings['type']) ? sanitize_key((string) $settings['type']) : 'info';
$type_class = in_array($type, ['info', 'warning'], true) ? 'is-' . $type : 'is-info';
echo '<div class="bpcab-badge ' . esc_attr($type_class) . '">...</div>';

Variante 2 – URL-Steuerelement hinzufügen und einen sauberen Link erstellen

Ein klickbarer Call-to-Action-Button (CTA) wird permanent angezeigt. Elementor bietet ein URL-Steuerelement mit den Optionen „In neuem Tab öffnen“ und „nofollow“.

// Contrôle URL
$this->add_control(
	'link',
	[
		'label' => esc_html__('Lien', 'bpcab'),
		'type'  => Controls_Manager::URL,
		'options' => ['url', 'is_external', 'nofollow'],
		'default' => [
			'url' => '',
		],
	]
);

// Dans render()
$link = $settings['link'] ?? [];
$url  = isset($link['url']) ? esc_url((string) $link['url']) : '';

if ($url) {
	$target = !empty($link['is_external']) ? ' target="_blank"' : '';
	$rel    = !empty($link['nofollow']) ? ' rel="nofollow noopener"' : ' rel="noopener"';

	echo '<a class="bpcab-badge" href="' . $url . '"' . $target . $rel . '>...</a>';
	return;
}

Hinweis: Wenn Sie einen Link in einem neuen Tab öffnen, behalten Sie ihn bei. noopener (Sicherheit).

Variante 3 — Ein Element nur auf bestimmten Seiten laden (noch strenger)

Bei komplexen Skripten können Sie die Widget-Abhängigkeit mit einer WordPress-Bedingung kombinieren. Zum Beispiel: nur auf Seiten (nicht der Startseite). Artikel):

public function register_frontend_scripts(): void {
	wp_register_script(
		'bpcab-badge',
		plugins_url('assets/js/badge.js', __FILE__),
		[],
		self::VERSION,
		true
	);

	// ⚠️ Ne faites pas wp_enqueue_script ici : Elementor gère l'enqueue via get_script_depends().
	// Si vous voulez vraiment empêcher le chargement sur certains contextes, vous pouvez deregister :
	if (!is_page()) {
		wp_deregister_script('bpcab-badge');
	}
}

Ich nutze es selten: Es kann überraschend sein, wenn ein Widget in einer Vorlage verwendet wird, die auch an anderer Stelle angezeigt wird. Testen Sie es gründlich.

Divi 5 / Elementor / Avada-Kompatibilität

Elementor

  • Das oben genannte Plugin integriert sich in Elementor, ohne von einem Theme abhängig zu sein.
  • Wenn Sie Elementor (Theme Builder)-Vorlagen verwenden, bleibt das Widget überall verfügbar.
  • Die Verfügbarkeit der Assets ist bedingt: ein guter Punkt auf stark frequentierten Baustellen.

Divi 5

Divi 5 verwendet nicht die Elementor-API. Ihr Widget wird daher nicht in Divi angezeigt, und das ist normal.

Wenn Sie dieselbe Komponente auf Divi-Seiten wiederverwenden möchten, empfehle ich eine „Builder-unabhängige“ Strategie:

  • Erstellen Sie einen WordPress-Shortcode (oder besser: einen Gutenberg-Block),
  • Fügen Sie es dann über ein Code-/Shortcode-Modul in Divi ein.
  • und Elementor als „UI-Overlay“ beibehalten, wenn es vorhanden ist.

Meiner Erfahrung nach ist dies der einzige Ansatz, der funktioniert, wenn es sich um einen Park mit mehreren Bauträgern handelt.

Avada (Fusion Builder)

Die gleiche Logik gilt: Avada verwendet keine Elementor-Widgets. Ihr Plugin bleibt jedoch nützlich, wenn die Website Elementor auf einigen Seiten einsetzt.

Für Avada ist das sauberste Vorgehen ebenfalls: Shortcode oder Block, dann das Element „Code Block“ / „Shortcode“ im Fusion Builder.

Prüfungen nach der Installation

  1. Aktivieren Sie das Plugin in Erweiterungsoptionen.
  2. Öffne eine Seite mit Elementor.
  3. Suchen Sie im Widget-Bereich nach der Kategorie BPCAB dann das Widget Abzeichen (BPCAB).
  4. Platziere es auf der Seite, bearbeite Titel, Text und Farben und veröffentliche es.
  5. Schauen Sie sich die Vorderseite an: Sie sollten Folgendes sehen: bpcab-badge.css geladen (und nicht auf Seiten, die das Widget nicht verwenden).

Schnelldiagnosetabelle

Symptom Mögliche Ursache Überprüfung Lösung
Die BPCAB-Kategorie erscheint nicht. Hook wurde nie ausgeführt (Elementor nicht geladen) Überprüfen Sie das. ELEMENTOR_VERSION ist definiert, und Elementor ist aktiv Elementor aktivieren, auf Konflikte/MU-Plugins prüfen
Schwerwiegender Fehler „Klasse ElementorWidget_Base nicht gefunden“ Die Widget-Datei wurde zu früh geladen / Elementor ist inaktiv PHP-Log und Stacktrace anzeigen Sie benötigen lediglich das Widget in elementor/widgets/register Après elementor/init
CSS/JS nicht geladen Nicht registrierte Handles oder fehlerhafter Registrierungshaken inspizieren wp_head / wp_footer + Konsole überprüfen after_register_styles/scripts et get_style_depends()
Das Widget wird zwar angezeigt, aber die Formatierung ist fehlerhaft. Caching (Plugin/CDN) oder aggressive Minifizierung Cache deaktivieren, CDN leeren, im privaten Browsermodus testen Dateien vom Cache/Minifizierung ausschließen, Versionsnummer erhöhen
Dynamisches Tag nicht gefunden Funktion nicht verfügbar (abhängig von Konfiguration/Pro) oder Hook nicht ausgelöst Prüfen Sie, ob das „Dynamische“ Bedienfeld für ein Textfeld vorhanden ist. Installieren/aktivieren Sie gegebenenfalls Elementor Pro oder entfernen Sie den Tag-Bereich.

Wenn es nicht funktioniert

  1. Bitte überprüfen Sie die Versionen. WordPress 6.9.4+, PHP 8.1+, Elementor aktuell. Eine veraltete PHP-Version verursacht Fehler. declare(strict_types=1) oder die Arten ?self.
  2. Protokollierung aktivieren in wp-config.php (in der Inszenierung):

    define('WP_DEBUG', true);

    define('WP_DEBUG_LOG', true);

    define('WP_DEBUG_DISPLAY', false);
  3. geöffnet wp-content/debug.log und suche nach „BPCAB“ oder „Elementor“.
  4. Vorübergehend deaktivieren Snippet-Plugins. Ich habe bereits ein Snippet „aus einem alten Elementor-Tutorial“ gesehen, das eine Klasse mit demselben Namen deklarierte und dadurch einen schwerwiegenden Konflikt verursachte.
  5. Cache leeren Plugin-Cache, Server-Cache, CDN, Browser-Cache. Bei Elementor kann aggressives Caching auch fehlende Ressourcen erhalten.
  6. Elementor CSS neu generieren (Falls Ihre Website die CSS-Generierungsoption verwendet). In Elementor finden Sie normalerweise eine Option zur CSS-Generierung unter „Tools/Performance“.
  7. Probieren Sie es mit einem neutralen Thema. (vorübergehend): Twenty Twenty-* oder ein schlankes Theme. Ein Theme kann Skripte/Stile abmelden.

Häufige Fallstricke und Fehler

Fehler Verursachen Lösung
Der Code wurde in die falsche Datei eingefügt. Hinzugefügt in functions.php anstelle eines Plugins Erstelle ein Plugin, versioniere es und aktiviere/deaktiviere es ordnungsgemäß.
„Analysefehler: Syntaxfehler“ Fehlendes Semikolon, zusätzliche geschweifte Klammer, unvollständiges Kopieren und Einfügen Überprüfen Sie die im Protokoll angegebene Zeile und verwenden Sie eine IDE mit PHP-Formatierung.
Hook Elementor ungeeignet Die Verwendung von init / wp_loaded ein Widget speichern Verwendung elementor/init puis elementor/widgets/register
„Klasse ElementorPlugin nicht gefunden“ Elementor wurde deaktiviert oder nach Ihrem Code geladen. überprüfen defined('ELEMENTOR_VERSION') und rufen Sie Elementor niemals vorher an elementor/init
CSS/JS nicht geladen Fehlerhafter Handle, fehlerhafter Hook oder Cache-/Minifizierungsfehler Speichern über after_register_styles/scripts, deklarieren Sie Abhängigkeiten über get_*_depends()Cache leeren
Namenskonflikt der Klasse Zwei Plugins deklarieren BPCAB_Widget_Badge (oder ein falsch konfigurierter Autoloader) Bei der Industrialisierung sollten Sie immer Präfixe und Namensräume verwenden.
Verwechslung von Aktion und Filter Sie versuchen, zu einer Aktie zurückzukehren. Aktionen: Nebenwirkungen. Filter: Geben einen Wert zurück. Überprüfen Sie den verwendeten Hook.
Direkte Tests in der Produktion Keine Staging-Umgebung, kein Backup Staging-, Backup- und Rollback-Plan (Plugin-Deaktivierung per FTP bei Bedarf)
Inkonsistente Permalinks/Vorlagen Sie testen mit einer anderen Vorlage als der gerenderten (Theme Builder). Prüfen Sie, welche Elementor-Vorlage tatsächlich angewendet wird, und leeren Sie den Cache.

Sicherheits-, Leistungs- und Wartungstipps

Sicherheit

  • Systematisches Entkommen Alles, was in HTML ausgegeben wird, muss dem Kontext entsprechend maskiert werden (esc_html, esc_attr, esc_url, wp_kses_post). Referenz : WordPress: Datenvalidierung.
  • Keine „kostenlosen HTML“-Optionen Für Benutzer ohne Administratorrechte. Auf Websites mit mehreren Autoren stellt dies eine XSS-Sicherheitslücke dar.
  • Keine PHP-Ausführung über das Widget (Es scheint offensichtlich, aber ich habe schon einige zusammengewürfelte "Code-Widgets" gesehen.)

Leistung

  • Bedingte Vermögenswerte get_style_depends() / get_script_depends() Es bietet das beste Verhältnis von Aufwand zu Nutzen.
  • Vermeiden Sie Schleifenanfragen in render()Wenn Sie Daten (Beiträge, Metadaten) laden müssen, speichern Sie diese im Cache (Transienten-/Objektcache) oder bereiten Sie sie über eine optimierte Abfrage auf.
  • Minimales CSS Ein Widget ist eine kleine Datei. Wenn Sie 20 davon haben, gruppieren Sie sie sinnvoll (behalten Sie aber die Bedingtheit bei).

Wartung

  • Version Verwenden Sie das Plugin (Git) und kennzeichnen Sie Ihre Releases mit Tags. Wenn Elementor eine API ändert, wissen Sie so, was Sie bereitstellen müssen.
  • Vermeiden Sie „alte“ Tutorials diese verwenden veraltete Hooks. Wenn Sie einen Codeausschnitt aus den Jahren 2021–2023 wiederverwenden, stellen Sie sicher, dass er mit der aktuellen Elementor-Version und WordPress 6.9.4 kompatibel ist.
  • Bereiten Sie eine Ausweichstrategie vor. Wenn Elementor deaktiviert ist, sollte Ihr Plugin „nichts tun“, ohne dabei einen fatalen Fehler auszulösen.

Ressourcen

FAQ

Funktioniert dieser Code mit WordPress 6.9.4?

Ja: Das Plugin folgt den Standardpraktiken von WordPress (Hooks, Enqueue) und ist für PHP 8.1+ ausgelegt. Das Hauptkompatibilitätsproblem bleibt die Elementor-Version (halten Sie diese auf dem neuesten Stand).

Warum nicht ein Snippets-Plugin verwenden?

Für einen schnellen Test ist es ausreichend. Für ein wiederverwendbares Elementor-Widget ist ein richtiges Plugin jedoch zuverlässiger: kontrolliertes Laden, übersichtliche Dateien, Versionsverwaltung und saubere Deaktivierung im Fehlerfall.

Mein Widget wird zwar angezeigt, befindet sich aber nicht in der richtigen Kategorie.

Prüfe das get_categories() umdrehen ['bpcab']und dass die Kategorie registriert ist über elementor/elements/categories_registered.

Wie füge ich mehrere Widgets hinzu?

Fügen Sie weitere Dateien hinzu zu includes/widgets/ und speichern Sie sie in register_widgets()Eine Datei = eine Klasse.

Wie kann ich das Laden von JavaScript vermeiden, wenn ich es nicht benötige?

Löschen get_script_depends() oder ein leeres Array zurückgeben. Das Widget soll so weit wie möglich ohne JavaScript funktionsfähig bleiben.

Können wir einen Autoloader (Composer) verwenden?

Ja, insbesondere bei mehr als zehn Widgets. Im WordPress-Kontext sollte man darauf achten, Composer nicht auf der finalen Website zu erzwingen. Üblicherweise wird stattdessen ein PSR-4-Autoloader in das Plugin integriert.

Warum verwenden? wp_kses_post() Für den Text?

Denn ein Textfeld kann HTML enthalten, wenn Elementor dies zulässt (oder wenn der Benutzer Inhalte einfügt). wp_kses_post() ermöglicht eine sichere Teilmenge, im Gegensatz zu einem Roh-Echo.

Das dynamische Tag wird nicht angezeigt: Ist das normal?

Das hängt von Ihrer Elementor-Konfiguration ab. Prüfen Sie, ob die „Dynamische“ Benutzeroberfläche für Ihre Felder verfügbar ist. Falls Ihre Website keine dynamischen Tags unterstützt, entfernen Sie den entsprechenden Abschnitt. elementor/dynamic_tags/register und die zugehörige Datei.

Wie kann ich ordnungsgemäß testen, ohne den Editor zu beschädigen?

Testen Sie im Staging-Modus, aktivieren Sie die Protokollierung und beginnen Sie mit einem minimalen Widget (gerendert + ein Steuerelement). Fügen Sie die Steuerelemente nacheinander hinzu. Elementor-Fehler sind oft nicht in der Benutzeroberfläche sichtbar, aber in der Konsole und im PHP-Protokoll erkennbar.

Ist es mit einem Divi/Avada-Child-Theme kompatibel?

Ja, da es sich um ein Plugin handelt. Das Widget wird jedoch nur in Elementor angezeigt. Für Divi/Avada verwenden Sie einen Shortcode oder einen Block, wenn Sie eine Komponente benötigen, die von mehreren Buildern genutzt werden kann.