Ad/iklan :







Search.Pencarian Menu

Add text send email to rh3252705.adda@blogger.com or Click this (Text porn Will delete) | Tambah teks kirim email ke rh3252705.adda@blogger.com atau Klik ini (Teks porno akan dihapus)
Total post.pos : 13631+

[Go Make Things] Web Components vs. State-Based UI

A while back, one of my readers suggested I do a comparison of how you would build the same component using a Web Component and state-based UI.

I kept putting it off, but with all of the hot drama around Web Components recently, now feels like the perfect time to do that. Let's dig in!

Want to learn how to write your own Web Components? I've got a whole guide on that on the Lean Web Club. Try it for free today.

The project

For this comparison, we're going to build a simple disclosure component.

We'll have a <button> and some content. When clicked/tapped/activated, the button will reveal the content. When activated again, it will hide it.

<button>Show Content</button>    <div>  	<p>Now you see me, now you don't!</p>  </div>

The ARIA Authoring Guidelines for this component are relatively simple…

  • The element that shows/hides the content should trigger the behavior when the Enter or Space keys are pressed.
  • The element that shows/hides the content should have an [aria-expanded] attribute on it, with a value of true when it's visible, and false when it's hidden.

There's also a note about the [aria-controls] attribute, but it's optional and a bit flaky, so we won't worry about that for this project.

The Web Component

For the Web Component version, we'll start with our HTML already rendered in the UI.

We'll wrap the content in a <show-hide> custom element. We'll add a [trigger] attribute to the element that toggles the show/hide behavior, and a [content] attribute to the content it shows/hides.

We'll also add the [hidden] attribute to the [trigger] to hide it by default, since it's useless until the JavaScript to control the behavior loads.

<show-hide>  	<button trigger hidden>Show Content</button>    	<div content>  		<p>Now you see me, now you don't!</p>  	</div>  </show-hide>

Inside our JavaScript, we'll define a new show-hide custom element, and create our class that extends the HTMLElement.

Hate writing this from scratch? I've got a boilerplate you can copy/paste over at the Lean Web Club.

Inside the constructor(), I looking for the [trigger] and [content], and save them to variables. If they don't exist, I'll bail early.

customElements.define('show-hide', class extends HTMLElement {    	/**  	 * Instantiate the Web Component  	 */  	constructor () {    		// Get parent class properties  		super();    		// Get the elements  		this.trigger = this.querySelector('[trigger]');  		this.content = this.querySelector('[content]');  		if (!this.trigger || !this.content) return;    	}    });  

Next, I setup the default UI.

I remove the [hidden] attribute from this.trigger, and set an [aria-expanded] attribute with a value of false. Then, I add the [hidden] attribute to this.content to hide it in the UI.

/**   * Instantiate the Web Component   */  constructor () {    	// ...    	// Setup default UI  	this.trigger.removeAttribute('hidden');  	this.trigger.setAttribute('aria-expanded', false);  	this.content.setAttribute('hidden', '');    }  

Finally, I add a click event listener to this.trigger. Because it's a <button> element, the click event will fire whether it's clicked, tapped, or activated with the Return or Enter key.

(This is why choosing the right HTML element for the job is so important. You get a ton for free out-of-the-box.)

I pass this, the current instance, into the addEventListener() method instead of a callback function.

As long as the object (JS classes are objects) has a handleEvent() method, it will be run automatically and receive the event as an argument. But it will also retain the context of this, which allows us to access the instance properties without have to use .bind() or other hacks.

/**   * Instantiate the Web Component   */  constructor () {    	// ...    	// Listen for click events  	this.trigger.addEventListener('click', this);    }  

Next, I create a handleEvent() class method.

The first thing I do is run event.preventDefault() to stop the <button> from triggering other actions (such as submitting a <form>, for example).

/**   * Handle events   * @param {Event} event The event object   */  handleEvent (event) {    	// Don't let the button trigger other actions  	event.preventDefault();    }  

If this.trigger has an [aria-expanded] value of true, the content is visible. I switch it to false, then add the [hidden] attribute to this.content to hide it.

Otherwise, I do the reverse to show the content instead.

/**   * Handle events   * @param {Event} event The event object   */  handleEvent (event) {    	// Don't let the button trigger other actions  	event.preventDefault();    	// If the content is expanded, hide it  	// Otherwise, show it  	if (this.trigger.getAttribute('aria-expanded') === 'true') {  		this.trigger.setAttribute('aria-expanded', false);  		this.content.setAttribute('hidden', '');  	} else {  		this.trigger.setAttribute('aria-expanded', true);  		this.content.removeAttribute('hidden');  	}    }  

And that's it!

Here's a demo.

State-Based UI

For simplicity, I'm going to use my own tiny state-based UI library, Reef, for this.

It's all vanilla JS, requires no transpiling, and can run from a CDN directly in the browser. It works more-or-less like how libraries like Solid and Preact work.

We'll start by creating a signal(), a reactive piece of data that will trigger a UI render whenever it's properties are updated.

We'll add an expanded property, and give it a default value of false.

// Create a signal  let data = signal({  	expanded: false  });  

Next, we'll create a template() that generates the HTML to display based on the data state.

We'll use the data.expanded property to control the value of the [aria-expanded] attribute and whether or not the content has the [hidden] attribute.

// Create a template function  function template () {  	return `  		<button aria-expanded="${data.expanded}">Show Content</button>    		<div ${data.expanded ? '' : 'hidden'}>  			<p>Now you see me, now you don't!</p>  		</div>`;  }  

Then, I tell Reef to inject the HTML from the template() into the #app element, and render an update whenever the data changes.

This is similar to the render() and signal() methods in Preact.

component('#app', template);  

Now, we need to detect when the <button> is clicked, and update the data.expanded property.

We'll create a toggle() method that reverses the value of the data.expanded attribute (swapping it from true to false or vice-versa).

// Toggle visibility  function toggle () {  	data.expanded = !data.expanded;  }  

Then, we'll add an onclick listener to our <button> that runs the toggle() event. For security reasons, we also need to tell Reef that toggle() is an allowed event.

// Create a template function  function template () {  	return `  		<button onclick="toggle()" aria-expanded="${data.expanded}">Show Content</button>    		<div ${data.expanded ? '' : 'hidden'}>  			<p>Now you see me, now you don't!</p>  		</div>`;  }    // Renders into the UI, and updates whenever the data changes  component('#app', template, {events: {toggle}});  

Now, whenever the <button> is clicked, the data.expanded value switches, and the UI is automatically updated.

Here's another demo.

Which one is better?

The Web Component has some distinct advantages.

The content is always accessible, even when JS isn't available, or before it's rendered.

Web Components are also self-instantiating, so you can add a dozen of them into the DOM, each with their own content, and never have to create a separate template or bind it to the DOM in your JavaScript like you do with state-based UI.

State-based UI starts to really shine when you have a handful of different elements that all update themselves based on some shared data set.

For my money, though, Web Components are the go-to choice for a majority of use cases.

Cheers,
Chris

Want to share this with others or read it later? View it in a browser.

Share :

Facebook Twitter Google+
0 Komentar untuk "[Go Make Things] Web Components vs. State-Based UI"

Back To Top