Dominate the Markup

Hidden gems of HTML5 and more

Dominate the Markup

Do you view HTML as a boring batch of <div>s? If yes, then allow me a few minutes. By the end of this piece, you'll have a brand new belief system.

I've tried to keep it concise and practical. So, grab a cup of whatever and enjoy! 😇


Data in < >

It is possible to associate data with an HTML element through data attributes, storing extra information that has neither a defined meaning nor any visual representation. Any attribute whose name starts with data- is called a data attribute. The following is a usage example:

<!-- Keep an eye on the dashes -->
<div id="box" data-item-name="pen" data-item-quantity="12">
    Lorem ipsum dolor sit amet. 
</div>

In JavaScript, all such data is available as key-value pairs in the dataset object—a property of the Element itself. The keys are the parts after data- in camel case (without dashes), which is the usual naming convention for an object's key. Here’s the code for all that English:

const box = document.getElementById('box');
const { itemName, itemQuantity } = box.dataset; // Every property is a string

console.log(itemName); // pen
console.log(itemQuantity); // 12

To all my React freaks out there, are you having a feeling of déjà vu? Haha, same pinch! For some reason, data-* attributes have an uncanny resemblance to the way we pass props (data) to a React component, except only the data is available in a props object.

Hmm. I think it’s time to stop giving the VIP treatment to React… 🤔

Location matters

While surfing the web, you must have come across a modal saying "the site is asking to know your location." Something happens behind the scenes, depending on whether you tap "Allow" or "Block."

Web apps often need a user's geographical location, for example, to show information relevant to their location. That need is fulfilled by the Geolocation API, a Web API that allows an app to retrieve a user's location data. For obvious privacy concerns, the data is made available only after the user's approval.

That being said, how do we implement it? Consider the markup snippet below. We have a <button> that listens for the onclick event. Once triggered, it initiates a script that is supposed to inject the location data into a <p>.

<div>
    <h1>Want to know your Coordinates?</h1>
    <!-- Clicking the button should show the modal -->
    <button type="button" onclick="tryLocationAccess()">Click Me</button>
    <p id="location-info">
        <!-- Permission granted, the location data should appear here -->
    </p>
</div>

The following is an outline of the required script:

const paragraph = document.getElementById('location-info');

function tryLocationAccess() {
    // Entry point to the API
    const geo = navigator.geolocation;
    if (geo) 
        return geo.getCurrentPosition(getLocation, (err) => paragraph.textContent = 'You Blocked Me!');    
    // If location services are NA
    return paragraph.textContent = 'Not supported by your browser!';
}

// Called when the location retrieval is successful
function getLocation(position) {        
    const { latitude, longitude } = position.coords;
    paragraph.innerHTML = `
        Your current coordinates are: 
        <br /><br /> 
        Latitude: ${latitude} 
        <br />
        Longitude: ${longitude}
    `;
}

Explanation:

  • The Geolocation API is accessible through the navigator.geolocation object. Provided the location services are supported (i.e., the if condition above evaluates to true), it prompts the browser to ask for permission to access the user's location data. If allowed, the browser will use the best available functionality on the device to access this information, such as GPS.

  • The getCurrentPosition method retrieves the device's location. A successful fetch invokes a callback, the first argument, with a position object. Its second argument is an error handler, called when the location retrieval fails.

  • In our example, the getLocation function is the success callback that receives a position object. It destructures the coords property of position to access the coordinates and then injects them into <p>.

Such a Drag

Drag and drop—grabbing something on a page and dragging it to a different location—is a common UI trick. But what goes on backstage is rarely discussed, so here I am.

The HTML Drag and Drop API is a Web API that facilitates apps to use the drag and drop feature in browsers. The following pen presents a basic implementation. First, try it out, and then take a peek at the code; I'll explain it in a jiffy 🙂

Explanation:

  • Specifying the draggable="true" attribute in an HTML element makes it draggable, such as img-box in the example.

  • img-box is associated with a dragstart event listener (the drag function), which is invoked when the user starts to drag the element. It receives a DragEvent object with a dataTransfer property that holds the event's data. The handler uses its setData method to specify the data (and its type) to be dragged.

  • The next job is to create a drop area. By default, browsers prohibit dropping something onto most elements. So, we listen for a dragover event to prevent this default behavior, making the element droppable, such as the dropzone box above.

  • Now that the dropzone is ready, we attach a drop event listener to it, which is called when the img-box is dropped on it. Among other things, the drop handler uses the getData method to retrieve the dragged data, i.e., the id img-box, gets the corresponding element, and appends it to the dropzone.

I hope it makes sense now.☺️

It's custom-made

Web Components, a suite of Web APIs, let you create custom elements, which are HTML elements with specific behavior defined by the author. This feature allows for writing reusable UI logic.

Say we want a custom element user-info that renders an h1 followed by a p element. To be more specific, we want a custom tag that would render the following markup:

<!-- Make it reusable -->
<h1>Atanu Sarkar</h1>
<p>I'm a Web Software Engineer</p>

A custom element is implemented as a class that extends the base class HTMLElement, as shown below:

class UserInfo extends HTMLElement {
    constructor() {
        super();
        this.innerHTML = `
            <h1>Atanu Sarkar</h1>
            <p>I'm a Web Software Engineer</p>
        `;
    }
}

// Registers the custom element, making it available on page
customElements.define('user-info', UserInfo);

Once the element is registered, we can use it like any other built-in element: <user-info> </user-info>, and we would see the desired markup.

Wait. There is an issue here! 😕

A custom element, by definition, is a piece of reusable functionality—drop it anywhere, and the result should be exactly the same. But in our code, if we set any global style for h1, it would also affect the heading in user-info, undermining its custom behavior.

Say hello to the Shadow DOM.

One word: encapsulation. Shadow DOM enables attaching a hidden DOM tree to a container element (shadow host) and having its internals isolated from the code running on the page. This shadow tree starts with a shadow root, underneath which any element can be attached, in the same way as the normal DOM.

The following pen presents the modified script needed to encapsulate user-info:

Here, the custom element itself serves as the host to which a shadow DOM is attached. Then, we create a container div with the desired markup and append it to the root. Voila! No more external influence.

One more thing before you go, the above is proof that it is not necessary to use fancy frameworks like React or Svelte to create custom elements, the so-called "components." The native HTML5 API is fully equipped for the job.

THE END.


Truly grateful for your time. I hope I was able to change your perspective toward HTML. See you soon!

Ta Ta! 💜