linicks.dev
linicks.dev logo

Date Format Selector

Thursday, January 30, 202512 days ago

Some time ago, I created a small application with interactive form controls that fed into the Date.toLocaleString() function. I generally prefer standardized functions over third party dependencies, but I found it hard to experiment with the Date.toLocaleString() function and build an intuition for using it.

The result of my effort is the Date Format Selector, which you can find hosted on GitHub Pages at https://nicholas-wilcox.github.io/date-format-selector/. You can also find the source code here.


At the time, I was also gaining interest in the standard Web Component APIs, so I took the opportunity to learn how to create web components with vanilla JavaScript. Because most of the parameters of Date.toLocaleString() can each take one of an enumerative set of values (for example, the hour, minute, and second fields can each be either "numeric" or "2-digit"), I knew I needed to create a component for a group of connected radio inputs.

I also wanted my HTML to be succinct, without much repetition or clutter caused by my custom JavaScript. The coded ended up looking something like this:

<form>
<fieldset>
<legend>Style Options</legend>
<radio-group name="dateStyle" data-values="short,medium,long,full"></radio-group>
<radio-group name="timeStyle" data-values="short,medium,long,full"></radio-group>
</fieldset>
</form>

<!-- Template for a radio group -->
<template id="radio-group-template">
<link href="./radio-group.styles.css" rel="stylesheet">
<div>
<h3 id="header"></h3>
<button id="clearButton">Clear</button>
</div>
</template>

<!-- Template for an individual radio selector -->
<template id="radio-input-template">
<label class="radio-input-container">
<input type="radio">
<span class="radio-label"><span>
</label>
</template>

The JavaScript itself is less elegant, but that's really because I'm not using any third-party libraries. While defining a custom element is as simple as extending the HTMLElement class and calling the customElements.define() function, you still need to implement the component, which means programmatically appending DOM nodes inside the component. This is why I defined the templates in the main HTML file, so the JavaScript module for the web component could copy those templates as needed.

The following code has been heavily truncated (the event handling code has been removed), but it should give you an impression of how you implement a web component if you aren't already familiar.

radio-group.js
const radioGroupTemplate = document.getElementById("radio-group-template");
const radioInputTemplate = document.getElementById("radio-input-template");

class RadioGroup extends HTMLElement {
get name() {
return this.getAttribute("name");
}

get values() {
return this.dataset.values.split(",");
}

connectedCallback() {
// Construct shadow root
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(radioGroupTemplate.content.cloneNode(true));

this.values.forEach((value) => {
const radioNode = this.createRadioInputNode(value);
shadowRoot.appendChild(radioNode);
});
}

createRadioInputNode(value) {
const radioNode = radioInputTemplate.content.cloneNode(true);
const radioInput = radioNode.querySelector("input");
radioInput.setAttribute("name", this.name);
radioInput.setAttribute("value", value);
radioNode.querySelector(".radio-label").innerText = value;
return radioNode;
}
}

customElements.define("radio-group", RadioGroup);

There's more to be said about how the application deals with locales and time zones, so please check out the code and the README if you are interested.