Commit d00a86a0 authored by Samuel Mortenson's avatar Samuel Mortenson Committed by Sam Mortenson
Browse files

Issue #3262367 by samuel.mortenson: Provide a version of data-type="attach"...

Issue #3262367 by samuel.mortenson: Provide a version of data-type="attach" that does not use jQuery
parent 12424283
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -193,6 +193,25 @@ $(this).on('click', function () {
})(jQuery, Drupal, drupalSettings);
```

Drupal Core has recently started moving away from jQuery, so if you'd like to
use vanilla JS with your attachments (wrapping them in "once()" instead of
"jQuery.once()") you can add the "data-vanilla" attribute to your script tag.
Note that when using vanilla JS, "element" refers to your current element, not
"this". For example:

```php
<template>
  <button data-sfc-id="click_counter">Clicked 0 times</button>
</template>

<script data-type="attach" data-vanilla>
  var count = 0;
  element.onclick = function () {
    this.innerText = 'Clicked ' + ++count + ' times';
  };
</script>
```

## Component caching

Cache metadata for components is collected/bubbled when their template is
+28 −0
Original line number Diff line number Diff line
<!--
  Components can use vanilla JS instead of jQuery when using data-type="...".

  To do so, set "data-vanilla" on any script tag. Note that this will make both
  data-type="attach" and data-type="detach" use vanilla JS. The default
  selector used for the "once()" call added around your code is
  [data-sfc-id="<plugin_id>"]. If you want to customize the selector your code
  attaches to, you can define the "$selector" variable.

  Instead of "this", use "element" to reference the current DOM node.

  Unlike the jQuery version, if you use data-type="detach" you will need to
  remove your listeners with removeEventListener, and remove the once() calls
  with: once.remove('sfcDetach', ...) and once.remove('sfcAttach', ...).

  If the "data-vanilla" attribute is not present, jQuery is used.
-->

<template>
  <div data-sfc-id="example_vanilla_js">Clicked 0 times</div>
</template>

<script data-type="attach" data-vanilla>
  var count = 0;
  element.onclick = function () {
    this.innerText = 'Clicked ' + ++count + ' times';
  };
</script>
+53 −19
Original line number Diff line number Diff line
@@ -46,6 +46,11 @@ class ComponentBase extends PluginBase implements ComponentInterface, ContainerF
   */
  const DETACH = NULL;

  /**
   * A boolean indicating if vanilla JS should be used for ATTACH/DETACH.
   */
  const VANILLA_JS = FALSE;

  /**
   * An optional array of library dependencies.
   */
@@ -179,11 +184,17 @@ class ComponentBase extends PluginBase implements ComponentInterface, ContainerF
      $library['js'][file_url_transform_relative(file_create_url("$directory/$name.js"))] = [];
    }
    if ($this->hasAttachments()) {
      $attachments = $this->getAttachmentData();
      $library['dependencies'][] = 'core/drupal';
      $library['dependencies'][] = 'core/drupalSettings';
      if (isset($attachments['vanilla_js']) && $attachments['vanilla_js']) {
        $library['dependencies'][] = 'core/once';
      }
      else {
        $library['dependencies'][] = 'core/jquery';
        $library['dependencies'][] = 'core/jquery.once';
      }
    }
    return $library;
  }

@@ -334,6 +345,27 @@ class ComponentBase extends PluginBase implements ComponentInterface, ContainerF
    }
    if ($this->hasAttachments()) {
      $attachments = $this->getAttachmentData();
      if (isset($attachments['vanilla_js']) && $attachments['vanilla_js']) {
        $js .= "(function (once, Drupal, drupalSettings) {\n";
        $js .= "  Drupal.behaviors.sfc_{$this->getId()} = {\n";
        if ($attachments['attach']) {
          $js .= "    attach: function attach(context, settings) {\n";
          $js .= "      once('sfcAttach', " . json_encode($attachments['selector']) . ", context).forEach(function (element) {\n";
          $js .= $attachments['attach'] . "\n";
          $js .= "      });\n";
          $js .= "    },\n";
        }
        if ($attachments['detach']) {
          $js .= "    detach: function detach(context, settings, trigger) {\n";
          $js .= "      once('sfcDetach', " . json_encode($attachments['selector']) . ", context).forEach(function (element) {\n";
          $js .= $attachments['detach'] . "\n";
          $js .= "      });\n";
          $js .= "    },\n";
        }
        $js .= "  }\n";
        $js .= "})(once, Drupal, drupalSettings);\n";
      }
      else {
        $js .= "(function ($, Drupal, drupalSettings) {\n";
        $js .= "  Drupal.behaviors.sfc_{$this->getId()} = {\n";
        if ($attachments['attach']) {
@@ -354,6 +386,7 @@ class ComponentBase extends PluginBase implements ComponentInterface, ContainerF
        $js .= "  }\n";
        $js .= "})(jQuery, Drupal, drupalSettings);\n";
      }
    }
    if ($js) {
      file_put_contents("$directory/$name.js", $js);
    }
@@ -373,6 +406,7 @@ class ComponentBase extends PluginBase implements ComponentInterface, ContainerF
      'selector' => $this::SELECTOR ? $this::SELECTOR : $this->getFallBackSelector(),
      'attach' => $this::ATTACH,
      'detach' => $this::DETACH,
      'vanilla_js' => $this::VANILLA_JS,
    ];
  }

+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ class SimpleComponent extends ComponentBase {
      'JS' => NULL,
      'ATTACH' => NULL,
      'DETACH' => NULL,
      'VANILLA_JS' => FALSE,
      'CSS' => NULL,
      'TEMPLATE' => '',
      'SELECTOR' => NULL,
@@ -94,6 +95,9 @@ class SimpleComponent extends ComponentBase {
    if (preg_match('/^<script[^>]*data-type="detach"[^>]*>([\s\S]+?)^<\/script>/im', $content, $matches)) {
      $file_data['DETACH'] = $matches[1];
    }
    if (preg_match('/^<script[^>]*data-vanilla[^>]*>/im', $content)) {
      $file_data['VANILLA_JS'] = TRUE;
    }
    if (preg_match('/^<style[^>]*>([\s\S]+?)^<\/style>/im', $content, $matches)) {
      $file_data['CSS'] = $matches[1];
    }
@@ -121,6 +125,7 @@ class SimpleComponent extends ComponentBase {
      'selector' => $this::SELECTOR ? $this::SELECTOR : $this->getFallBackSelector(),
      'attach' => $data['ATTACH'],
      'detach' => $data['DETACH'],
      'vanilla_js' => $data['VANILLA_JS'],
    ];
  }

+7 −0
Original line number Diff line number Diff line
<script data-type="attach" data-vanilla>
  alert('foo');
</script>

<template>
  <div class="foo">Hello world!</div>
</template>
Loading