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.
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>
<p id="next_post" title='Get New Blog' style='cursor: pointer;'>Click ♻️ 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.