Loading README.md +19 −0 Original line number Diff line number Diff line Loading @@ -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 Loading modules/sfc_example/components/example_vanilla_js.sfc 0 → 100644 +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> src/ComponentBase.php +53 −19 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; } Loading Loading @@ -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']) { Loading @@ -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); } Loading @@ -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, ]; } Loading src/Plugin/SingleFileComponent/SimpleComponent.php +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class SimpleComponent extends ComponentBase { 'JS' => NULL, 'ATTACH' => NULL, 'DETACH' => NULL, 'VANILLA_JS' => FALSE, 'CSS' => NULL, 'TEMPLATE' => '', 'SELECTOR' => NULL, Loading Loading @@ -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]; } Loading Loading @@ -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'], ]; } Loading tests/modules/sfc_test/components/simple_vanilla_js.sfc 0 → 100644 +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
README.md +19 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
modules/sfc_example/components/example_vanilla_js.sfc 0 → 100644 +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>
src/ComponentBase.php +53 −19 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; } Loading Loading @@ -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']) { Loading @@ -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); } Loading @@ -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, ]; } Loading
src/Plugin/SingleFileComponent/SimpleComponent.php +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class SimpleComponent extends ComponentBase { 'JS' => NULL, 'ATTACH' => NULL, 'DETACH' => NULL, 'VANILLA_JS' => FALSE, 'CSS' => NULL, 'TEMPLATE' => '', 'SELECTOR' => NULL, Loading Loading @@ -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]; } Loading Loading @@ -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'], ]; } Loading
tests/modules/sfc_test/components/simple_vanilla_js.sfc 0 → 100644 +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>