---
url: 'https://dxpr.com/nl/hc/documentation/learn-dxpr-builder/development/custom-elements'
title: 'Aangepaste elementen maken | DXPR'
author:
  name: Jurriaan
  url: 'https://dxpr.com/users/jurriaan'
date: '2026-10-24T02:33:11+00:00'
updated: '2026-10-24T09:09:48+00:00'
copyright: 'Copyright DXPR. All rights reserved.'
type: article
summary: |-
  Elk DXPR Builder-element is een JavaScript-object gepusht op
  window.dxprBuilder.dxpr_elements. De editor maakt van elk object
  een elementklasse met de tabbladen Style en Animation er al
  aan. Deze pagina toont hoe de meegeleverde elementen zijn gedefinieerd in
  dxpr_builder/dxpr_elements.js, hoe een module zijn eigen
  definitie aan de editor toevoegt en hoe de resulterende HTML eruitziet.
  Er zijn geen Twig-sjablonen per element aan de serverkant; elementen
  renderen in de browser.
tags:
  - 'Bouw erop voort'
image: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
published: true
og:
  site_name: DXPR
  updated_time: '2026-10-24T09:09:48+0000'
  'image:alt': 'DXPR, powered by Drupal: AI-powered Drupal for marketers.'
schema:
  '@context': 'https://schema.org'
  '@graph':
    -
      mainEntity: {  }
    -
      '@type': TechArticle
      headline: 'Aangepaste elementen maken'
      description: |-
        Elk DXPR Builder-element is een JavaScript-object gepusht op
        window.dxprBuilder.dxpr_elements. De editor maakt van elk object
        een elementklasse met de tabbladen Style en Animation er al
        aan. Deze pagina toont hoe de meegeleverde elementen zijn gedefinieerd in
        dxpr_builder/dxpr_elements.js, hoe een module zijn eigen
        definitie aan de editor toevoegt en hoe de resulterende HTML eruitziet.
        Er zijn geen Twig-sjablonen per element aan de serverkant; elementen
        renderen in de browser.
      image:
        '@type': ImageObject
        url: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
        width: '1200'
        height: '630'
      datePublished: '2026-10-24T02:33:11+0000'
      dateModified: '2026-10-24T09:09:48+0000'
      author:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      mainEntityOfPage: 'https://dxpr.com/nl/hc/documentation/learn-dxpr-builder/development/custom-elements'
      articleSection: 'Bouw erop voort'
    -
      '@type': WebPage
      '@id': 'https://dxpr.com/nl/hc/documentation/learn-dxpr-builder/development/custom-elements'
      description: |-
        Elk DXPR Builder-element is een JavaScript-object gepusht op
        window.dxprBuilder.dxpr_elements. De editor maakt van elk object
        een elementklasse met de tabbladen Style en Animation er al
        aan. Deze pagina toont hoe de meegeleverde elementen zijn gedefinieerd in
        dxpr_builder/dxpr_elements.js, hoe een module zijn eigen
        definitie aan de editor toevoegt en hoe de resulterende HTML eruitziet.
        Er zijn geen Twig-sjablonen per element aan de serverkant; elementen
        renderen in de browser.
      breadcrumb:
        '@type': BreadcrumbList
        itemListElement:
          -
            '@type': ListItem
            position: 1
            name: Home
            item: 'https://dxpr.com/nl'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
      inLanguage: nl
---
[Help Center](/nl/hc)/[Documentatie](/nl/hc/documentatie) /[Learn DXPR Builder](/hc/documentation/learn-dxpr-builder) /[Extend](/hc/documentation/learn-dxpr-builder/extend) /[Bouw erop voort](/hc/documentation/learn-dxpr-builder/extend/build-on-it) # Aangepaste elementen maken.

 Learn DXPR BuilderExtendBouw erop voort

   ## In this guide

 Related guides

- [Hooks API](/nl/hc/documentation/learn-dxpr-builder/development/hooks-api)
- [DXPR Builder themen](/nl/hc/documentation/learn-dxpr-builder/development/architecture)

Elk DXPR Builder-element is een JavaScript-object gepusht op `window.dxprBuilder.dxpr_elements`. De editor maakt van elk object een elementklasse met de tabbladen Style en Animation er al aan. Deze pagina toont hoe de meegeleverde elementen zijn gedefinieerd in `dxpr_builder/dxpr_elements.js`, hoe een module zijn eigen definitie aan de editor toevoegt en hoe de resulterende HTML eruitziet. Er zijn geen Twig-sjablonen per element aan de serverkant; elementen renderen in de browser.

## Hoe de editor elementen registreert

De editor laadt zes scripts in deze volgorde, gedefinieerd in `dxpr_builder_library_info_build()` in `dxpr_builder.module` als de `dxpr_builder/editor.builder`-library:

1. `dxpr_templates.js`: twig.js-sjablonen aan de clientkant gecompileerd uit `dxpr_builder/templates/`.
2. `dxpr_global.js`: gedeelde helpers en de CKEditor 5-integratie.
3. `dxpr_events.js`
4. `dxpr_param_types.js`: pusht één object per inputtype op `window.dxprBuilder.dxpr_param_types`.
5. `dxpr_elements.js`: maakt de array `window.dxprBuilder.dxpr_elements`en pusht één definitie per element.
6. `dxpr_builder.js`: de editor. Wanneer dit bestand wordt uitgevoerd roept het `create_dxpr_elements()` aan, dat over `window.dxprBuilder.dxpr_elements` loopt en voor elke definitie:
    - roept `register_animated_element(base, is_container, Class)` aan, zodat de klasse `AnimatedElement` uitbreidt (tabblad Style, tabblad Animation, HTML ID, HTML classes en plaatsingsparameters);
    - hangt de overgeërfde parameters aan de `params` van de definitie;
    - converteert elke parameter via `make_param_type()`, dat `param.type` matcht met de geregistreerde parametertypen;
    - mixt de eigenschappen van de definitie in het klasseprototype.

Een definitie moet dus op de array staan nadat `dxpr_elements.js` heeft gedraaid en voordat `dxpr_builder.js` draait. Alle zes bestanden laden met het attribuut `defer`, dus de documentvolgorde van de scripttags is de uitvoeringsvolgorde.

## De elementdefinitie

Dit is het Alert-element van `dxpr_builder/dxpr_elements.js`(bron: `dxpr_builder/build/dxpr-elements/elements/az_alert.js`), ingekort tot de eigenschappen die elk element nodig heeft. Het origineel stelt ook `el_class`, een `onCancel`-handler en een `focusout`-listener in.

```javascript
window.dxprBuilder.dxpr_elements.push({
  base: "az_alert",
  name: Drupal.t("Alert"),
  nameEn: "Alert",
  icon: "az-custom-icon az-alert-icon",
  largeSettingsWindow: true,
  hidden: false,
  params: [
    {
      type: "textarea",
      heading: Drupal.t("Message"),
      param_name: "content",
      value: Drupal.t(
        "This is a placeholder text. Your actual content will go here. Edit this to include your own information.",
      ),
    },
    {
      type: "dropdown",
      heading: Drupal.t("Type"),
      param_name: "type",
      description: Drupal.t("Select message type."),
      getText: (opt) => opt.text,
      getValue: (opt) => opt.value,
      getDefault: (param) => param.value["alert-success"].value,
      value: {
        "alert-success": { text: Drupal.t("Success"), value: "alert-success" },
        "alert-info": { text: Drupal.t("Info"), value: "alert-info" },
        "alert-warning": { text: Drupal.t("Warning"), value: "alert-warning" },
        "alert-danger": { text: Drupal.t("Danger"), value: "alert-danger" },
      },
      passthrough: true,
    },
  ],
  show_settings_on_create: true,
  has_content: true,
  render(...args) {
    const state = {
      class: `az-element az-alert alert ${this.attrs.type} ${this.get_el_classes()} ${this.get_content_classes()}`,
      style: this.attrs.style,
      message: this.attrs.content,
    };
    this.dom_element = this.baseclass.prototype.render_template(
      "elements/alert/alert",
      state,
    );
    this.baseclass.prototype.render.apply(this, args);
  },
  save_history() {
    const container = this.get_my_container();
    container.save_container_history(Drupal.t("Edit alert"));
  },
});

```

| Eigenschap | Doel |
|---|---|
| `base` | Machinenaam. Hij wordt in de opgeslagen HTML geschreven als `data-azb="az_alert"` en gebruikt om de klasse te vinden wanneer de pagina opnieuw wordt geparsed. Houd de prefix `az_` aan. |
| `name`, `nameEn` | Label in de elementkiezer en zijn niet-vertaalde vorm. |
| `icon` | CSS-klassen voor het kiezerpictogram. De meegeleverde pictogrammen zijn gedefinieerd in `dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss`; een aangepast element heeft eigen CSS nodig voor zijn pictogramklasse. |
| `category` | Kiezer-tabblad. De lay-outelementen gebruiken `Drupal.t("Layout")`; blokken en views gebruiken `"CMS"`. Laat het weg voor het standaard inhoudstabblad. |
| `is_container` | `true` wanneer het element andere elementen bevat. |
| `largeSettingsWindow` | Opent het instellingendialoogvenster in de grotere maat. |
| `hidden` | Verbergt het element voor de kiezer terwijl het renderbaar blijft. |
| `params` | Instellingen getoond in het dialoogvenster. Zie hieronder. |
| `show_settings_on_create` | Opent het instellingendialoogvenster zodra het element wordt neergezet. |
| `has_content` | Het element heeft een bewerkbaar inhoudsgebied (`dom_content_element`). |
| `render(...args)` | Bouwt `this.dom_element` uit `this.attrs`, en roept daarna de basis-`render` aan. |
| `save_history()` | Pusht een undo-vermelding met een label. |
| `parse_attrs_from_dom(element)` | Optioneel. Leest parameterwaarden terug uit opgeslagen markup, samen gebruikt met `skip_data_azb` en `skip_params` door elementen zoals Image die waarden in de `<img>`-attributen bewaren in plaats van `data-azat-*`. |

### Parameters

Elke vermelding in `params` heeft:

| Sleutel | Betekenis |
|---|---|
| `type` | Een van de geregistreerde parametertypen (volgende sectie). |
| `heading` | Veldlabel. |
| `param_name` | Sleutel in `this.attrs` en, in opgeslagen HTML, het attribuut `data-azat-<param_name>`. `content` is speciaal: het is de elementbody, geen attribuut. |
| `value` | Standaardwaarde. Voor `dropdown` is het een object met opties met `getText`-, `getValue`- en `getDefault`-callbacks. |
| `description` | Hulptekst onder het veld. |
| `tab` | Groepeert het veld in een benoemde accordeon, bijvoorbeeld `Drupal.t("SEO")`. |
| `can_be_empty` | Bewaar een lege string in plaats van het attribuut te laten vallen. |
| `safe` | Sla URL-codering van de opgeslagen waarde over. |

De parametertypen zijn de `type`-waarden gepusht op `window.dxprBuilder.dxpr_param_types` in `dxpr_builder/dxpr_param_types.js`: `boxmodel`, `checkbox`, `checkboxes`, `colorpicker`, `datetime`, `dropdown`, `dxb_slider`, `html`, `icon`, `image`, `images`, `link`, `links`, `numericfield`, `row_layout`, `saved_datetime`, `social_links`, `textarea`, `textfield` en `video`. Een type dat je niet registreert valt terug op een gewone tekstinput (`BaseParamType`).

Hergebruik geen `param_name` die elk element overerft, zoals `color`, `background`, `el_class`, `hash` of de `an_*`-animatienamen. Het tabblad Style bezit `color` al, dus een tweede parameter met die naam wordt nooit naar `data-azat-color` geschreven.

### Wat wordt opgeslagen

`update_data()` in `dxpr_builder.js` schrijft het element naar HTML:

- `data-azb="<base>"` op het rootelement;
- `data-azat-<param_name>="<value>"` voor elke parameter waarvan de waarde afwijkt van zijn standaard, URL-gecodeerd tenzij de parameter `safe: true` zet;
- de Style-tabbladparameters als inline `style` en klassen, nooit als `data-azat-*`-attributen;
- `data-azcnt="true"` op het inhoudselement wanneer `has_content` is ingesteld.

Kijkers die de editor niet krijgen ontvangen deze HTML zonder zijn `data-az*`-attributen: `DxprBuilderService::parseDocumentForCleanup()`stript ze, behalve een vaste lijst die hij behoudt op geanimeerde elementen en op ingebouwde dynamische elementen zoals Carousel. Als het element JavaScript of CSS nodig heeft voor bezoekers, zet je een `data-dxpr-builder-libraries="<key>"`-attribuut op zijn root: `parseDocumentForTemplateLibrary()` koppelt `dxpr_builder/elements.<key>` voor elke key zonder slash. De keys zijn de `elements.*`-libraries in `dxpr_builder.libraries.yml`; een key met een slash wordt alleen gehonoreerd wanneer het de `library` is van een geregistreerde pictogrammenset.

## Minimaal werkend voorbeeld

Het voorbeeld voegt een "Notice"-element toe dat een alinea met een gekozen kleur rendert. Het bouwt zijn DOM direct omdat de gecompileerde twig.js-sjablonen in `dxpr_templates.js` deel zijn van de editorbundle en niet door een andere module kunnen worden uitgebreid.

1. Maak `mymodule/js/notice-element.js`:

    ```javascript
    window.dxprBuilder.dxpr_elements.push({
      base: "az_notice",
      name: Drupal.t("Notice"),
      nameEn: "Notice",
      icon: "az-custom-icon az-alert-icon",
      params: [
        {
          type: "textfield",
          heading: Drupal.t("Text"),
          param_name: "text",
          value: Drupal.t("Notice text"),
        },
        {
          type: "colorpicker",
          heading: Drupal.t("Colour"),
          param_name: "notice_color",
          value: "#0d6efd",
        },
      ],
      show_settings_on_create: true,
      render(...args) {
        const el = document.createElement("div");
        el.className = `az-element az-notice ${this.get_el_classes()}`;
        if (this.attrs.style) {
          el.setAttribute("style", this.attrs.style);
        }
        const p = document.createElement("p");
        p.style.color = this.attrs.notice_color;
        p.textContent = this.attrs.text;
        el.appendChild(p);
        this.dom_element = el;
        this.baseclass.prototype.render.apply(this, args);
      },
      save_history() {
        this.get_my_container().save_container_history(Drupal.t("Edit notice"));
      },
    });

    ```
2. Voeg het bestand in de editorlibrary in tussen `dxpr_elements.js` (gewicht `-4`) en `dxpr_builder.js` (gewicht `-3`) in `mymodule.module`:

    ```php
    /**
     * Implements hook_library_info_alter().
     */
    function mymodule_library_info_alter(array &$libraries, string $extension): void {
      if ($extension === 'dxpr_builder' && isset($libraries['editor.builder'])) {
        $path = '/' . \Drupal::service('extension.list.module')->getPath('mymodule');
        $libraries['editor.builder']['js'][$path . '/js/notice-element.js'] = [
          'weight' => -3.5,
          'preprocess' => FALSE,
          'attributes' => ['defer' => TRUE],
        ];
      }
    }

    ```

    `preprocess: FALSE` en `defer` matchen de vermeldingen die de module definieert voor zijn eigen bestanden, zodat het bestand zijn plek in de volgorde behoudt of editorassets nu uit het CDN of uit een lokale build komen.
3. Wis de cache en open een builderpagina:

    ```bash
    drush cr

    ```
4. Voeg het element uit de kiezer toe, stel zijn tekst en kleur in en sla op. Het veld bewaart:

    ```html
    <div class="az-element az-notice" id="..." data-azb="az_notice"
         data-azat-text="Hello" data-azat-notice_color="%23198754">
      <p style="color: #198754;">Hello</p>
    </div>

    ```

    Een bezoeker krijgt dezelfde markup zonder de `data-az*`-attributen. De alinea behoudt zijn inline kleur, dus voor dit element is geen bezoekerslibrary nodig.

## Limieten om te kennen

- **Profielen vermelden geen aangepaste elementen.** De elementchecklist in een builderprofiel komt uit een vaste lijst in `src/Form/DxprBuilderProfileForm.php` en `src/Service/Handler/ProfileHandler.php`. Elementen buiten die lijst worden nooit door een profiel verborgen.
- **Pictogrammen voor de kiezer** hebben CSS nodig van je module; de `az-custom-icon`-klassen meegeleverd door de builder dekken alleen de meegeleverde elementen.
- **`DxprBuilderService::getDxprElementsFolders()`** en zijn `hook_dxpr_builder_elements_folders_alter()` geven een lijst met `elements`-mappen in thema's terug, maar niets in de editor leest die lijst. Het is geen registratiroute.
- **Style- en Animation-tabbladen** worden voor je toegevoegd door `register_animated_element()`; herdefinieer die parameters niet.

## Wat nu?

- [Hooks API](/nl/hc/documentation/learn-dxpr-builder/development/hooks-api) voor de PHP-hooks die utility-klassen en pictogrammensets toevoegen.
- [Architectuur](/nl/hc/documentation/learn-dxpr-builder/development/architecture) voor hoe de editor en de veldformatter in elkaar passen.