js

auto-wrap.js
document.querySelectorAll('iframe:not(.no-auto-wrap)').forEach((iframe) => {
  // wraps every iframe for responsive embedding, runs once on load
});
index.html
<div class="skip-marker"></div>
<div id="widget-container">
  <script src="https://example.com/widget-embed.js"></script>
</div>
mark-async-embed.js
const pending = new Set(document.querySelectorAll('.skip-marker'));

function check() {
  pending.forEach((marker) => {
    const iframe = marker.nextElementSibling?.querySelector('iframe:not(.no-auto-wrap)');
    if (iframe) {
      iframe.classList.add('no-auto-wrap');
      pending.delete(marker);
    }
  });
  if (pending.size === 0) observer.disconnect();
}

const observer = new MutationObserver(check);
observer.observe(document.body, { childList: true, subtree: true });
check(); // catches iframes that already exist

 

SetValueIfEmptyCommand.php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Ajax;

use Drupal\Core\Ajax\CommandInterface;

/**
 * Ajax command: fill empty form elements with a predefined value.
 *
 * This command targets one or more form elements via a CSS selector and sets
 * their value only if the current value is empty (after trimming).
 *
 * The command also triggers `input` and `change` events so Drupal behaviors and
 * other listeners react as if the user entered the value manually.
 */
final class SetValueIfEmptyCommand implements CommandInterface {

  /**
   * The Ajax command name used by Drupal.AjaxCommands.
   */
  public const COMMAND = 'setValueIfEmpty';

  /**
   * Default selector used when none is provided.
   */
  public const DEFAULT_SELECTOR = '[data-my-fill-target]';

  /**
   * Creates a command instance.
   *
   * @param string $fillValue
   *   The value to set when the target element is empty.
   * @param string|null $targetSelector
   *   CSS selector for the target elements. If omitted, DEFAULT_SELECTOR is used.
   */
  public function __construct(
    protected string $fillValue,
    protected ?string $targetSelector = null,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function render(): array {
    return [
      'command' => self::COMMAND,
      'selector' => $this->targetSelector ?? self::DEFAULT_SELECTOR,
      'value' => $this->fillValue,
    ];
  }

}
media_fill_if_empty.js
/**
 * @file Field empty check.
 */
(function (Drupal) {
  'use strict';

  Drupal.AjaxCommands.prototype.setValueIfEmpty = function (ajax, response) {
    const targetSelector = response && response.selector ? response.selector : '';
    const fillValue = response && typeof response.value === 'string' ? response.value : '';

    if (!targetSelector) {
      return;
    }

    const elements = document.querySelectorAll(targetSelector);
    for (const element of elements) {
      let currentValue = '';

      if (typeof element.value === 'string') {
        currentValue = element.value.trim();
      }
      else if (element.isContentEditable) {
        currentValue = (element.textContent || '').trim();
      }

      if (currentValue !== '') {
        continue;
      }

      if (typeof element.value === 'string') {
        element.value = fillValue;
      }
      else if (element.isContentEditable) {
        element.textContent = fillValue;
      }
      else {
        continue;
      }

      element.dispatchEvent(new Event('input', { bubbles: true }));
      element.dispatchEvent(new Event('change', { bubbles: true }));
    }
  };

})(Drupal);
my_module.libraries.yml
set_value_if_empty:
  js:
    js/set_value_if_empty.js: {}
  dependencies:
    - core/drupal
    - core/drupal.ajax
MyResponse.php
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\my_module\Ajax\SetValueIfEmptyCommand;

$response = new AjaxResponse();

// Example: fill a media name field if it's empty.
$fill_value = 'foo-bar';

// Selector for a specific element in this use-case.
$target_selector = '[data-drupal-selector$="my_field"]';

$response->addAttachments([
  'library' => [
    'my_module/set_value_if_empty',
  ],
]);

$response->addCommand(new SetValueIfEmptyCommand($fill_value, $target_selector));

return $response;
print.js
const printButtons = document.querySelectorAll('[aria-labelledby^="print"]');

printButtons.forEach(printButton => {
    printButton.addEventListener('click', () => {
        // Example Traversing
        const article = printButton.parentElement.parentElement.previousElementSibling;

        article.setAttribute('id', 'show-for-print');
        body.classList.add('hide-from-print');
        window.print();

        // Toggle the class and ID after 1 second
        setTimeout(() => {
            article.removeAttribute('id');
            body.classList.remove('hide-from-print');
        }, 1000);
    });
});
print.scss
@media print {
  // add this to the body
  .hide-from-print {
     visibility: hidden;
  }

  // Add this to the target element
  #show-for-print {
    visibility: visible;
  }
}
22.05.2025 | Holger Weischenberg

Print only a part of a page

cssMediaQuery.js
// Retrieve all computed styles from the root HTML element (:root)
const rootStyles = getComputedStyle(document.documentElement);

// Get the value of the CSS variable --Breakpoint.
// Use a fallback (e.g., 768px) if the variable is not defined.
const breakpoint = rootStyles.getPropertyValue('--Breakpoint').trim() || '768px';

// Create a MediaQueryList based on the breakpoint value
const mediaQuery = window.matchMedia(`(min-width: ${breakpoint})`);

// Function that runs when the viewport is at or above the breakpoint
function handleDesktopLayout() {
    console.log("Viewport is at or above the breakpoint.");
}

// Function that runs when the viewport drops below the breakpoint
function handleMobileLayout() {
    console.log("Viewport is below the breakpoint.");
}

// Early return for the initial state:
// If the viewport does NOT meet the breakpoint requirement, run mobile logic and exit
if (!mediaQuery.matches) {
    handleMobileLayout();
    // No desktop logic needed at this point
} else {
    handleDesktopLayout();
}

// Listen for changes in viewport size relative to the breakpoint
mediaQuery.addEventListener('change', (event) => {
    if (event.matches) {
        // The viewport just became equal to or larger than the breakpoint
        handleDesktopLayout();
    } else {
        // The viewport just became smaller than the breakpoint
        handleMobileLayout();
    }
});

formDataManipulation.js
// Modifies form data on submit.
form.addEventListener("formdata", (e) => {
  const formData = e.formData;
  geoLocationFieldNames.forEach((name) => {
    formData.delete(name);
  });
});

29.01.2026 | Peter Gerken

Manipulate Form Data before Submit

getObjectFromDataByKeyValue.js
/**
 * Loads an object from another object by value of a certain key.
 *
 * @usage 
 *   data = [
 *     'foo': {
 *         'id' => 5,
 *         'label' => 'Foo'
 *     },
 *     'bar': {
 *         'id' => 6,
 *         'label' => 'Bar'
 *     }
 *   ]
 *   foo = getObjectFromDataByKeyValue('id', 5, data)
 *
 * @param key
 *   The key we are comparing our value with.
 * @param value
 *   The value the key should have.
 * @param data
 *   The data you want to search in.
 * @returns {unknown}
 */
function getObjectFromDataByKeyValue(key, value, data) {
  return Object.values(data).find(o => o[key] === value)
}