Create standard and reliable custom elements
The custom elements created can be used in any framework and can be created in a variety of ways. When you want to expose your custom elements, you need to design carefully.
Element name
The first thing to consider when creating a custom element is to define a suitable element name, because duplicate element names are not allowed in the entire document. So you should define a clear naming method in your project:
Their class name should correspond to the element name, because directly using the constructor is also a way to create elements:
Constructable element
In imperative calls, it is common to configure properties in constructor arguments:
To avoid static attribute defined in the declarative form being overwritten in the constructor, you should only set the passed attributes in the constructor:
Attribute or Property
When using Gem to create a custom element, you can define Attribute and Property. Both of them can pass data to the element, and both can make them "observable", that is, when their values are changed, they will trigger element updates. But Attribute can be expressed by Markup, machine readable, and can be directly edited in the browser DevTools, and Attribute has default values, which is very convenient when used inside the element, so if you can use the data represented by Attribute, try to use Attribute. Property is only used for data types not supported by Attribute.
When a property needs to support templates, <slot> (ShadowDOM) or non-responsive Property can be used:
TIP
Backwards compatibility can be ensured in the same way when deprecating properties:
@customElement('portal-module-profile') class PortalModuleProfileElement extends GemElement { /**@deprecated */ @property data?: Item[]; @property items?: Item[]; get #items() { return this.items || this.data; } }
Public or Private
When using TypeScript to write Gem elements, their fields and methods are all public by default. Although you can use the private modifier to mark them as private, they are still public in JavaScript and can be accessed outside the element. In order to prevent users from accidentally using these fields and methods, you should use Private Fields in JavaScript:
Another advantage of using private fields is that they won't have the same name as GemElement/HTMLelement attributes or methods, which has high benefits when developing complex elements.
addEventListener or onclick
You can use event handler property when adding native DOM event listeners inside elements:
Never use this method, because they have many disadvantages:
- According to ES Semantics, it will not work
- Can be overridden and cancelled outside the element
So you should use addEventListener to register event handlers:
Handling element errors
When an error occurs in an element, the error should be propagated in an event mode, so that the external event listener can be used to handle the error:
Performance
When writing an element template, can add inline styles, which works in Shadow DOM(not in Light DOM):
This is equivalent to creating a <style> element in each <my-element>. If it is a static style, you should try to use Constructable Stylesheet , It has better performance and lower memory usage:
If you need to render many instances at once, you can use @async to create asynchronous rendering elements, which can avoid blocking the main thread during rendering and guarantee 60fps:
Styling
Suppose you use the <my-element> element defined above somewhere else, and for some reason add the hidden attribute in the hope of temporarily hiding it:
You will look that the hidden attribute does not take effect because the custom element's style display: contents will override the browser style display: none,
so the :host style should be defined carefully to avoid making it difficult for external use, such as using :where:
In addition, use @layer to solve the problem of multi-state style coverage of elements; use CSS Nesting simplified stylesheets.
Accessibility
When users use custom elements, they can use the role,aria-* attributes to specify the semantics of the element:
Use ElementInternals to define the default semantics of custom elements, use delegatesFocus or @aria focusable:
NOTE
delegatesFocusor@aria.focusableelements with thedisabledattribute will not trigger theclickevent just like native elements.Resources: