Intuition: Weird JavaScript Behaviour in SDC
When working on a project, I’ve setup this SDC named test-js
in a Radix theme with the following files: (see: test-js-sdc-example-files.zip)
|
|
The content of the files are as follows:
|
|
|
|
|
|
I then did some editing to the page-content.twig
component, to replace the default {{content}}
block with a multiple of this test-js
SDC component like the following, so the SDC I declared earlier can be used (for quick testting purpose):
|
|
I am expecting to see multiple of the component in the content area of html, as well as multiple console log [test-js] ...
; Though I predicted the multiple of components on html, superizingly the JavaScript file in the SDC only get executed once ! And looking at the network history, the JavaScript seems to be parsed right after when the theme library is attached, and is executed once when it is parsed/attached.
Upon some research, it is found that the better practice is to control the execution of JavaScript via wrapping in Drupal.behavior
API , which allows you to attach functions to be executed at certain time during the life-cycle of the page, and to pass in context, and settings (variables) to the JavaScript files.
When you write plain JS without wrapping them in Drupal.behavior
, you would have:
No automatic exeuction on “page load”:
- it might not run once when dom loads, it will immedicately when the code is parsed, and this may happen before hte dom loads in (for instance when attached in the head: example)
**No automatic execution on “page update” **
- if you are using
Drupal.behaviour
, JavaScript will automatically re-apply to conent when the content is loaded dynamically using AJAX. For instance, when you change the “show XYZ results in a page” setting in a view, theDrupal.behavior
will automacially rerun when the new DOM chunks are attached. (This ensures you script re-applies to new element.
- if you are using
No ability to pass variabels to JavaScript
- you will not be able to read the
context
andsettings
variables from the JavaScript files
- you will not be able to read the
Drupal JavaScript API (Drupal Behavior)
According to the official Drupal documentation on: link:
Any object defined as a property of
Drupal.behaviors
will get itsattach()
method called when the DOM has loaded both initially and after any AJAX calls. drupal.js has a$(document).ready()
function which calls theDrupal.attachBehaviors()
function, which in turn cycles through theDrupal.behaviors
object calling every one of its properties, these all being functions declared by various modules as above, and passing in the document as the context. On AJAX loads the same thing happens except the context is only the new content that the AJAX call loaded.Drupal Behaviors are fired whenever attachBehaviors is called. The context variable that is passed in can often give you a better idea of what DOM element is being processed, but it is not a sure way to know if you are processing something again. Using once with “context” is a good practice because then only the given context is searched and not the entire document. This becomes more important when attaching behaviors after an AJAX request.
Basic Example (executed once)
With the same testJS
single-directory-component, we will alter its Twig and JavaScript to the following:
(files can also be found at: test-js-behavior-basic-example.zip)
|
|
|
|
Advanced Example (execute upon update)
Next we will attempt to use drupal behavior to re-run/re-attach JavaScript when a view is updated via AJAX, first we’ll create the view below, with a filter where we can use to change the order of the items in this view via “authored on” date, and pager to display the result in smaller chunks:
And by default we have the following files to beign with:
- [node.twig](node.twig (orginal).zip) (using the node-teaser SDC to display the teaser article)
- [node-teaser](node-teaser (original).zip) (the folder containing the node-teaser SDC component)
Let’s make some edition to include a javascript which will prepend the teaser’s random id next to its title:
|
|
|
|
(full code can be found at: [node-teaser (with behavior).zip](node-teaser (with behavior).zip) )
(Not Recommended) Using Document.Ready
for Deferring Execution
When you just want to control the first execution of the JavaScript without the need of “re-applying when page updates” and “pass context&settings variables”, it is also fine to just use one of the below:
|
|
With that said, for finer control over the execution, it is still recommended to always use the Drupal behavior instead. And you can achieve the exact same effect of running on document load using behavior, see below:
|
|
Read more about this on: https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview#s-drupalbehaviors-compared-to-jquerydocumentready-for-deferring-execution