How to: The life cycle of a custom elements
We can unite callback methods to create an element, unite it to the DOM, change attributes, delete an element from the DOM, and change the parent document. We will use this to install the spoiler logic: the component will contain a button with the text “Collapse” / “Expand” and apart with the original contents of the tag. Section visibility will be controlled by clicking on the button or attribute value. Button text can also be customized via attributes.
The callback for creating a component is the class constructor. To make it work, you must first call the parent constructor via super. In the constructor, you can set the markup, hang event handlers, do some other preparative work. In the constructor, as in other methods, this will refer to the DOM element itself, and due to the fact that our custom element extends the HTMLElement, this has methods such as querySelector and properties such as classList.
Add values for the button texts in the constructor, markup of the component and attach the handler to the click on the button, which will substitution the proximity of the opened attribute. Let us analyze in detail each part of the constructor.
Super calls the constructor of the class HTMLElement. This is, in this case, a mandatory activity if we need an element constructor.
This.text - since this is an object, we can unite our own properties. In this case, I will store in the text object auxiliary texts that are displayed on the button.
This.innerHTML will set down the markup of our DOM element. In this case, we use the text specified above.
This.querySelector ("button"). addEventListener will unite a click event handler that sets or removes the opened attribute. We will work with it as with a logical value - the spoiler is either open or closed, thus, the attribute is either there or not. In the handler, we will check for the presence of an attribute through comparison with null, and then either set or delete the attribute.
Now when you click on the created button, the opened attribute will change. So far, changing the attribute does nothing. Before turning to this topic, we change the code a bit.
Agree, work through the properties is much more convenient? We implement the same mechanism with our attribute opened so that its value can be obtained and installed. To do this, we use the possibility of getters and setters of properties in classes:
For string properties (as for any built-in id for any elements for links), the getter and setter will look it simpler, but the idea remains.
You can also unite that such a "reflexion" may not always be a useful performance. For example, for form elements, the value attribute works.
Now we have an attribute, it can change its value when a button is clicked, but no more useful action occurs. We could add useful code to hide and display the element in the click handler, but then it would be very problematical to change the element's visibility, for example, with another, external JS code.
Instead, you can attach a handler to change the value of the attributes using the attributeChangedCallback method. It will be called each time the attribute is changed, so it will be possible to control the component through the attributes both from the inside and the outside.
The method applies three parameters: attribute name, old value, new value. Since calling this method to change ALL attributes would be irrational from the point of view of performance, it only works if you change the properties that are listed in the observed attributes static class of the current class.
Our component must respond to the change of three attributes - opened, text-when-open and text-when-close. The first will affect the display of the spoiler, and the other two will control the button text.
Now we add the attributeChangedCallback method itself, which, depending on the attribute that has changed, will either change the visibility of the content and display the button text or change the button text and display it if necessary. To do this, use the switch on the first argument of the method.
Make note of that the attributeChangedCallback method works even when the required attributes are present on the element. That is, if our component is inserted into the markup at once with the opened attribute, the spoiler will indeed be opened since The attributeChangedCallback will work at once after the constructor. Thus, no more work on processing the initial value of the attributes in the constructor is necessary (unless, of course, the attribute is traceable).
Now our component works! When a button is clicked, the value of the opened attribute changes, then the attributeChangedCallback callback is activated, which in turn controls the visibility of the content. Managing the conditions through attributes and attributeChangedCallback allows you to control the initial conditions (we can add opened to the markup immediately if we want to show an open spoiler) or command the state from the outside (any other JS code can set or remove an attribute from our element and it will be processed). As a bonus, we can customize the text of the control button. View demo result in new chrome!
The main functionality is ready; these are the most used features of user elements. Now consider those callbacks that are used less.
When the element is inserted into the DOM tree, the connectedCallback method is triggered. If the element was already in the markup at the time of registration, or it is created by inserting an HTML string, the constructor will work, if necessary, the attributeChangedCallback, and then the connect callback. This callback can be used if, for example, you need to know the information about the parent in the DOM tree, or we want to optimize our component and put off some kind of heavy code before the element is used. But, two things to remember: first, if the constructor is triggered once for one element, then connectedCallback is triggered every time an element is inserted into the DOM, and second, attributeChangedCallback can trigger before connectedCallback, so if you postpone the creation of markup from constructor to connectedCallback, this can lead to an error. The method can be used to assign event handlers or for other heavy operations, such as connecting to a server.
As you can track the insertion of an element in the DOM, you can track and delete. The disconnectedCallback method handles this. It works, for example, when an element is removed from the DOM using the remove method. Note that if an element is removed from the DOM tree, but you have a link to the element, it can again be inserted into the DOM, and the connectedCallback will re-activate. When deleting, you can, for example, stop updating the data in the component, delete timers, delete event handlers assigned to connectedCallback, or close the connection to the server. Please note that disconnectedCallback does not guarantee the execution of its code - for example, when a user closes a page, the method will not be called.
The most rarely used callback is the adoptedCallback method. It is triggered when an element changes the property ownerDocument. This happens, for example, if you create a new window and move an element into it.
About custom item you can get more information here.