One of the best things I've learned in my career is the Separation of Concerns Principal which guides us in setting up some rules for our web component (WC) library.
Rules
Here's the first three rules we will follow:
- Use convention over configuration
- All functions or methods to have only one concern.
- Function or Method names must be carefully determined. This communicates what it does. It's a huge clue to developers down the road.
Convention over Configuration
Our convention, in order:
- Declare a custom Element:
customElements.define(
Define the tag name such as
bing-search
and remember it must have a hyphen. The tag name should always describe its sole function.Declare a class and extend from HtmlElement as shown:
customElements.define(
"bing-search",
class extends HTMLElement {
constructor() {
super();
this.initializeElements();
this.addEventHandlers();
Add a constructor with the first line being this:
super();
This is a requirement of WC.Create generic method names that will be used in all other WCs where needed.
Functions Have One Responsibility
Don't write monolithic code unless you want a new job.
We can avoid this by using the Single Responsibility Principle; which works at both the ECMA module layer as well as the class and function layers.
The initializeElements Function
Notice this method initializes components by creating them and then adding them to the WC.
It does not do anything else!
initializeElements() {
this.bingsearch = "bingsearch";
this.input = document.createElement("input");
this.input.id = this.bingsearch;
this.input.title = this.bingsearch;
this.input.type = "input";
this.input.value = this.getAttribute("searchstring");
this.button = document.createElement("input");
this.button.type = "submit";
this.button.value = "Submit";
this.bingsearchsubmit = "bingSearchSubmit";
this.button.id = this.bingsearchsubmit;
this.appendChild(this.input);this.appendChild(this.button);
}
The addEventHandlers Function
addEventHandlers() {
this.addEventListener("change", function (ev) {
let element = ev.target;
if (ev.target.id == this.bingsearch) {
this.OpenSearch();
return;
}
});
this.addEventListener("click", function (ev) {
let element = ev.target;
if (ev.target.id == this.bingsearchsubmit) {
this.OpenSearch();
return;
}
});
}
Both of these methods are different ways of doing the same thing, that's why we have a function named OpenSearch.
The OpenSearch Function
This function has a single responsibility, but was called in two different ways; therefore, it must be able to service both callers.
//searchPattern = "https://www.bing.com/search?q=";
OpenSearch() {
let searchtext = this.input.value;
let replaced = searchtext.replace(" ", "+");
let url = `${this.searchPattern}${replaced}`;
let win = window.open(url);
}
The GUI
The Result
How to Reuse this WC
In your HTML just import the bingsearch.js script, and make a reference to <bing-search>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="bingsearch.js" defer></script>
<title>Display Grid</title>
</head>
<body>
<h1>Bing Search</h1>
<bing-search searchstring="single responsibility"></bing-search>
</body>
</html>
The Element View
Notice anything missing? Well yes we should always include ids and or names and titles on our custom elements.
Top comments (0)