Lately, everyone is jumping on the Web component train for framework-agnostic UI Elements. While I am doing my reading on this, It looks a native way to write custom elements by the browser. But I have a question like. how different it is to js way of extending div(placeholders) to enhance and give some functionality we need.
For Ex:
If I want a table component
Web Component: <my-table></my-table>
---- Corresponding code in JS --- Compile to js file and use in script externally
JS Way: <div id="my-table"></div>
---- code in JS ---- Compile to JS and use in script tag externally
I am taking the ag-grid example to make it clear
ex: ag-grid https://www.ag-grid.com/javascript-grid/
<div id="myGrid" style="height: 200px; width:500px;" class="ag-theme-alpine"></div>
new agGrid.Grid(gridDiv, gridOptions);
What's the difference and benefits of one over the other? Please help
My first reaction was,
What is the difference between ES5 and ES6? (unless you are still doing ES3)
There are so many differences, it is quickly going to be an Apples and Apples comparison.
Read all blogs out there, play with Custom Elements, learn.
TL;DR
Use DOM Elements with Javascript - Order is required (1 and 2 can be switched):
Use Custom Elements API: - Order does not matter:
Create Custom DOM Elements
Load JavaScript
Suppose Customer wanted THREE grids on the page:
You add 3 DIVs:
<div id="myGrid1" .../>
<div id="myGrid2" .../>
<div id="myGrid3" .../>
To activate them you must also add:
new agGrid.Grid(document.getElementById("myGird1")
new agGrid.Grid(document.getElementById("myGird2")
new agGrid.Grid(document.getElementById("myGird3")
This goes horribly wrong if the JavaScript executes before the DOM elements exist
So you add an event listener:
window.addEventListner("onload",()=>ActivateGrids());
Note how you had to add (some type of) global function here;
so cross your fingers no-one else is going to overwrite your precious code;
Thinking about above implications, you decide to play it safe:
Why doesn't it work?
You fire F12, debug, and slam your head... you made a typo in de ID.. grid
and gird
2 days later...
Darn! Customer wants a 4th grid!
Before he bugs you for a 5th, you decide to refactor your ActivateGrids function to handle any number of grids in the page
[...document.querySelectorAll('[id*="MyuniQueIDgridComponent_KUT!"]')].forEach(...)
3 weeks later Customer has a very simple request, You can do this in half an hour, he says. We want to change the background color, with an attribute on the DIV
You are stumped!
Changing properties with DIV attributes!?!
Why can't they just call the JavaScript Grid instance?!?
You tried to educate Customer years ago; Semantic HTML has NO Future!
Only hire SENIOR Web Developers... but he wouldn't listen
What is the point with <my-grid background=green></my-grid>
When you can do it the cool programmers way:
document.getElementById("myGird1").mySetPropperty("background","green");
But Customer pays your mortgage;
so you learn about a MutationObserver, and stick it on the BODY tag.
(This one sentence is easily written but will take a serious effort coding!)
5 months later the customer says he doesn't want to hire you anymore because adding/removing Grids in their SPA cause a Memory leak.
All of the above is (only slightly) easier with a Framework..
But you want to build a RE-USABLE Component, NOT an Application built with Components
So you decide to go with the Native Custom Elements API
You thought about using Lit or Stencil or Hybrids or Component Libray X;
but decided against them.
Using Native JavaScript takes only a couple extra lines of code,
but foremost you do not want to introduce a Dependency (you recall the early jQuery days when there where dozen of alternatives and you had to scrap a $$$ project because a developer choose Mootools)
Native JavaScript will work as long as JavaScript is supported in the Browser.
You design how (end)user will use your component:
<KCB-grid rows=3 columns=5 background=color ></KCB-grid>
You then program the component.
I took the KCB- namespace from your Twitter handle
This namespace (like your global function) needs to be unique in the page
See code below, it is a text string, so can be created dynamically!
<KCB-Grid-Customer rows=3 columns=5 background=color ></KCB-grid-Customer>
Which works in any modern Browser
There is a polyfill for IE11; but frankly, avoid IE11.
You stopped supporting IE9 years ago. There is a moment in time to stop supporting IE11, that moment is now.. even Microsoft said so over a year ago
Works with all Frameworks and Libraries
(except in React you need to do extra work..
because Facebook still tries to force their 'standard' onto the rest of the world )
https://custom-elements-everywhere.com/libraries/react/results/results.html
There are no onload
issues, it doesn't matter if elements are declared before or after script. Custom Elements auto upgrade.
you can do .innerHTML = "<KCB-Grid></KCB-Grid>"
at any time.
Customer wanted that also,
but hired another programmer to do it with the Custom Element below
No CSS Selector uniqueness issues... and likely not do BEM at all
rows
and columns
can be changed at any time
Run the code snippet, click the grids
<template id="KCB-GRID">
<!-- use TEMPLATES! don't mess with JSX, CSSinJS or HTML in textstrings! -->
<style>
#grid { /* no BEM, no forced unique IDs or style names */
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
grid-auto-rows: minmax(min-content, max-content);
gap: .2em;
}
#grid div {
background: var(--background);
text-align: center;
}
</style>
<div id=grid></div>
</template>
<KCB-Grid columns=5 rows=3 background=#FF9933></kcb-grid>
<KCB-Grid columns=3 rows=3 background=white ></kcb-grid>
<KCB-Grid columns=4 rows=3 background=#138808></kcb-grid>
<script>
customElements.define('kcb-grid', class extends HTMLElement {
static get observedAttributes() {
return [ "columns", "rows" ] // only these can be changed at any time
}
constructor() {
super().attachShadow({ mode: 'open' })
.append(document.getElementById(this.nodeName).content.cloneNode(true));
this.grid = this.shadowRoot.querySelector("#grid");
}
connectedCallback() {
const random = (x, y) => Math.floor(Math.random() * (y + 1 - x) + x);
this.setProperty("background");
this.grid.onclick = () => {
this.setAttribute("columns", random(2, 6));
this.setAttribute("rows", random(2, 4));
}
}
attributeChangedCallback(name, oldValue, newValue) {
let columns = this.setProperty("columns");
let rows = this.setProperty("rows" );
this.grid.innerHTML = "<div>Hello World!</div>".repeat(columns * rows);
}
setProperty(name, value = this.getAttribute(name)) {
this.grid.style.setProperty("--" + name, value);
return value;
}
disconnectedCallback() {} //clean memory, remove listeners, etc.
});
</script>
Cue Tom Lehrer's Elements song: https://www.youtube.com/watch?v=AcS3NOQnsQM
<slot></slot>
::part
<img is=flag-india>
displays a SVG flag