<div class="btn-container">
<h1>Custom Built-In Element Examples</h1>
<div class="basic-example example">
<h2>Basic Examples</h2>
<button is="my-button" id="regular-button" btntext="Example Button"></button>
<button is="my-button" id="secondary-button" btntype="secondary" btntext="Secondary Button"></button>
<button is="my-button" id="block-button" btntype="primary" btntext="Block Button" block></button>
</div>
<div class="props-and-attributes example">
<h2>Change Attributes and Props</h2>
<button is="my-button" id="attr-button" btntext="Attributes Button"></button>
<button is="my-button" id="props-button" btntype="secondary" btntext="Properties Button"></button>
<button is="my-button" id="bool-button" block btntext="Boolean Props"></button>
<div class="input-wrapper">
<label for="attr-input">Change attribute button text</label>
<input id="attr-input" />
<button id="attr-change-trigger">Change</button>
</div>
<div class="input-wrapper">
<label for="properties-input">Change properties button text</label>
<input id="properties-input" name="properties-input" />
<button id="properties-change-trigger">Change</button>
</div>
<div class="input-wrapper">
<label for="bool-checkbox">Toggle Block Button</label>
<input id="bool-checkbox" type="checkbox" checked name="bool-checkbox" />
</div>
</div>
</div>
<script>
const regularButton = document.getElementById("regular-button");
const secondaryButton = document.getElementById("secondary-button");
const blockButton = document.getElementById("block-button");
for (const button of [regularButton, secondaryButton, blockButton]) {
button.addEventListener("click", evt => {
console.log("You clicked button with text: " + evt.currentTarget.btntext)
})
}
const attrInput = document.getElementById("attr-input");
const attrChangeButton = document.getElementById("attr-change-trigger");
const attrButton = document.getElementById("attr-button");
const propertiesInput = document.getElementById("properties-input");
const propertiesChangeButton = document.getElementById("properties-change-trigger");
const propertiesButton = document.getElementById("props-button");
const boolButton = document.getElementById("bool-button");
const boolCheckbox = document.getElementById("bool-checkbox");
attrChangeButton.addEventListener("click", () => {
attrButton.setAttribute("btntext", attrInput.value);
});
propertiesChangeButton.addEventListener("click", () => {
propertiesButton.btntext = propertiesInput.value;
});
if (!boolCheckbox.checked) {
boolButton.removeAttribute("block");
}
boolCheckbox.addEventListener("change", evt => {
if (evt.currentTarget.checked) {
boolButton.setAttribute("block", true);
} else {
boolButton.removeAttribute("block");
}
});
</script>
.btn-container,
.props-and-attributes {
display: flex;
flex-direction: column;
}
.btn-container {
gap: 2rem;
}
.input-wrapper {
width: 40%;
}
#slot-button::part(button) {
background-color: red;
}
#bool-button {
--button-default-color: green;
}
:root {
--button-default-color: blue;
--button-secondary-color: gray;
}
.my-button {
max-width: 200px;
border: 0;
border-radius: 1rem;
padding: 1rem;
min-width: 100px;
text-align: center;
}
.my-button--secondary {
background-color: var(--button-secondary-color);
color: white;
}
.my-button--primary {
background-color: var(--button-default-color);
color: white;
}
.my-button.my-button--block {
max-width: 100%;
width: 100%;
display: block;
}
// A class defining a custom my-button element.
// Here we explicitly extend HTMLButtonElement
class MyButton extends HTMLButtonElement {
#btnType = "primary"
#btnText = ""
#block = false
static observedAttributes = ["btntext", "btntype", "block"]
constructor() {
super();
}
// Called when an attribute changes.
attributeChangedCallback(attr, oldVal, newVal) {
if (attr === "btntext" && oldVal !== newVal) {
this.#btnText = newVal;
}
if (attr === "btntype" && oldVal === newVal) {
this.#btnType = newVal;
}
if (attr === "block") {
if (newVal === null) {
this.#block = false
this.classList.remove("my-button--block");
} else {
this.#block = true
this.classList.add("my-button--block");
}
}
this.render();
}
// When the component is attached to the DOM.
connectedCallback() {
this.#btnType = this.getAttribute("btntype") || this.#btnType; // Keep primary default
this.#btnText = this.getAttribute("btntext");
this.#block = this.hasAttribute("block");
this.render();
}
// Getters and setters.
// Allows getting/setting properties on the element using element.{property}
// Similar to how you might grab the value off an input using input.value.
set btntype(value) {
this.#btnType = value;
this.render();
}
get btntype() {
return this.#btnType;
}
set btntext(value) {
this.#btnText = value;
this.render();
}
get btntext() {
return this.#btnText;
}
// Method for generating a class name based on the btntype attribute.
#getBtnTypeClass() {
if (this.#btnType === "secondary") {
return "my-button--secondary";
}
return "my-button--primary";
}
// Not a lifecycle hook!
// Custom helper method for rendering the component.
render() {
const buttonClasses = ['my-button', this.#getBtnTypeClass(), (this.#block ? 'my-button--block' : '')].filter(clazz => !!clazz);
this.textContent = this.#btnText;
this.classList.add(buttonClasses);
}
}
// Defined slightly differently
// Notice we explicitly extend button here.
customElements.define("my-button", MyButton, { extends: "button" }); // Register the element with the registry.
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.