Blogring – Web Component using Vanilla JavaScript

I wanted to write a simple web component to display the Blogring. Currently, I have embed.html, I use it in an iframe, inside which we get the data to create the content and set it as the innerHTML of a div.

Screenshot of current iframe embed.

I just wanted a fancy tag that would embed the Blogring.

<blog-ring></blog-ring>

I could achieve that using the Web components. You will have to include a JavaScript file before adding the tag to HTML. This Javascript file has the defnition of the Web component that we are going to use in HTML. It's vanilla JavaScript, and it doesn't depend on any framework. Some important parts are below explained. At the bottom of the you have the whole JS file.

First I am defining a new class that will contain everything I want to do. It extends HTMLElement to create a new tag.

class BlogRing extends HTMLElement { }

But in the HTML we want to use the the tag <blog-ring></blog-ring>. That association we are making in last line. Where I am defining a customElement called blog-ring defined by the class BlogRing.

window.customElements.define('blog-ring', BlogRing);

Now that that the definition is over. Let's look at our class. The class has a constructor() where I am calling this.attachShadow which sets and returns this.shadowRoot. This will be the root of our web component's DOM (shadow). You can use this DOM like any other DOM. Also you can define any other variables you want in the constructor.

    constructor() {
        super();
        this.attachShadow({
            mode: "open"
        });
        this.totalPosts = 0;
    }

Now we need to add actual content. For this, we will use one of the life cycle callbacks - connectedCallback(). It gets called when our HTMLElement is added to the DOM. So when the HTMLElement is added to the DOM. I want to set the base content using a Template (by cloning the Template and setting it to shadowRoot). Get the data and update it. That is exactly what I am doing in async connectedCallback.

    async connectedCallback() {
        this.shadowRoot.appendChild(template.content.cloneNode(true));

        //Get the metatadata of the blogring in sync
        //Most important one is total number of blogs
        //So then we can get one of the random blog.
        await this.getMetaData();        

        // Get a random Post and 
        this.getRandomBlog();

        this.shadowRoot.querySelector('#next_post').addEventListener("click", (event) => {
            this.getRandomBlog()
        });
    }

I am getting metadata in sync because I want the this.totalPosts to be set before we call this.getRandomBlog();. Hence async, await usage.

    /* This gets the meta data. We actually have to call this only once.
    *  We need to call this before we get the random blog
    */
    async getMetaData() {
        console.log("Get meta data");
        const response = await fetch("https://data.thejeshgn.com/blogring/meta");
        const json = await response.json();
        this.totalPosts = json['total'];
    }

When getRandomBlog gets the data. It calls renderBlogring, which actually sets the values in the shadowRoot using innerHTML.

    getRandomBlog() {
        let random_blog = Math.floor((Math.random() * this.totalPosts) + 1)
        console.log("random_blog",random_blog)
        return new Promise((res, rej) => {
            fetch('https://data.thejeshgn.com/blogring/' + random_blog)
                .then(data => data.json())
                .then((json) => {
                    this.renderBlogring(json);
                    res();
                })
                .catch((error) => rej(error));
        })
    }

    renderBlogring(data) {
        this.shadowRoot.querySelector('#link').href = data["url"];
        this.shadowRoot.querySelector('#link').innerHTML = data["title"];
        this.shadowRoot.querySelector('#description').innerHTML = data["description"];
    }

The complete JavaScript which I call blog-ring-tag.js is below. The HTML below just imports it and then uses blog-ring tag.

const template = document.createElement('template');

template.innerHTML = `
        <div id='blogcard'>
            I recommend <a target='_blank' id='link' href=''></a>. <span id="description"></span> 
            &nbsp;<p id="next_post" title='Get New Blog' style='cursor: pointer;'>Click &#x267b;&#xfe0f; for random blog.</p>
        
        </div>
        `

class BlogRing extends HTMLElement {

    constructor() {
        super();
        this.attachShadow({
            mode: "open"
        });
        this.totalPosts = 0;
    }
    

    async connectedCallback() {
        this.shadowRoot.appendChild(template.content.cloneNode(true));

        //Get the metatadata of the blogring in sync
        //Most important one is total number of blogs
        //So then we can get one of the random blog.
        await this.getMetaData();        

        // Get a random Post and 
        this.getRandomBlog();

        this.shadowRoot.querySelector('#next_post').addEventListener("click", (event) => {
            this.getRandomBlog()
        });
    }

    /* This gets the meta data. We actually have to call this only once.
    *  We need to call this before we get the random blog
    */
    async getMetaData() {
        console.log("Get meta data");
        const response = await fetch("https://data.thejeshgn.com/blogring/meta");
        const json = await response.json();
        this.totalPosts = json['total'];
    }


    getRandomBlog() {
        let random_blog = Math.floor((Math.random() * this.totalPosts) + 1)
        console.log("random_blog",random_blog)
        return new Promise((res, rej) => {
            fetch('https://data.thejeshgn.com/blogring/' + random_blog)
                .then(data => data.json())
                .then((json) => {
                    this.renderBlogring(json);
                    res();
                })
                .catch((error) => rej(error));
        })
    }

    renderBlogring(data) {
        this.shadowRoot.querySelector('#link').href = data["url"];
        this.shadowRoot.querySelector('#link').innerHTML = data["title"];
        this.shadowRoot.querySelector('#description').innerHTML = data["description"];
    }

}

window.customElements.define('blog-ring', BlogRing);

And the HTML is very simple

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
    <script type="text/javascript" src="blog-ring-tag.js"></script>
</head>
<body>
    <blog-ring></blog-ring>
</body>

</html>

You can see the running example here. Please remember when you run it locally or on your domain you might face CORS issues.


You can read this blog using RSS Feed. But if you are the person who loves getting emails, then you can join my readers by signing up.

Join 2,241 other subscribers