Introduction
This component consists in a image viewer with a description button.
Let's start by creating the structure of our web component. Create a file viewerLab.js:
class viewerLab extends HTMLElement {
//Here will be all the functionality.
}
window.customElements.define('viewer-lab', viewerLab);
Step 1: Add a constructor and a getter for noinfo attribute.
class viewerLab extends HTMLElement {
constructor () {
super();
}
get noinfo() {
return this.hasAttribute('noinfo');
}
}
window.customElements.define('viewer-lab', viewerLab);
Note that the noinfo attribute is when you do not want to show the description button.
Step 3: Add a function to activate a specific image
class viewerLab extends HTMLElement {
//Step 1
//Step 2
activeImage(e){
var ch = this.children;
var cant = ch.length;
for (var i = 0; i < cant; i++) {
ch[i].style.display = 'none';
}
ch[e].style.display = 'block';
this.shadowRoot.querySelector('#counter').innerHTML = (e+1)+'/'+(cant);
}
}
window.customElements.define('viewer-lab', viewerLab);
Note that the activeImage function has one param:
- e: Is the number of the activated image. Starting in 0.
Step 4: Create a function to add an image
class viewerLab extends HTMLElement {
//Step 1
//Step 2
//Step 3
addImage(e){
this.appendChild(e);
this.populateChildren();
//show the arrows
this.shadowRoot.querySelector('#leftA').style.display = 'block';
this.shadowRoot.querySelector('#rightA').style.display = 'block';
}
}
window.customElements.define('viewer-lab', viewerLab);
Note that the addImage function has one param
- e: Is the image element to add.
Step 5: Create a function that takes all <img> elements from a html element in order to add them to the web component. If the <img> elements have a data-info attribute, it will be passed to the new <img> children that is being added to the component.
class viewerLab extends HTMLElement {
//Step 1
//Step 2
//Step 3
//Step 4
loadImgFrom(ele){
var el = ele.querySelectorAll('img');
this.style.display = 'block';
this.innerHTML = '';
for (var i = 0; i < el.length; i++) {
var src = el[i].getAttribute('src');
var newPic = document.createElement('img');
newPic.setAttribute('slot','images');
newPic.setAttribute('src',src);
//if have data-info
if(el[i].getAttribute('data-info'))
newPic.setAttribute('data-info',el[i].getAttribute('data-info'));
//adding to the component
this.addImage(newPic);
}
}
}
window.customElements.define('viewer-lab', viewerLab);
Note that the loadImgFrom function has one param
- ele: Is the html element (example:<div>) that must have <img> children inside of it.
Step 6: Add a function that will be fired when click on description button
class viewerLab extends HTMLElement {
//Step 1
//Step 2
//Step 3
//Step 4
//Step 5
infoClick(title,fn){
this.shadowRoot.querySelector('#info').innerHTML = title;
this.shadowRoot.querySelector('#info').addEventListener('click', function func(event) {
fn(event);
});
}
}
window.customElements.define('viewer-lab', viewerLab);
Note that the infoClick function has two param
- title: The button text.
- fn: Is the function that will fired when click the button.
Step 8: Add a function connectedCallback
It will be fired when the component is created
class viewerLab extends HTMLElement {
//Step 1
//Step 2
//Step 3
//Step 4
//Step 5
//Step 6
//Step 7
connectedCallback () {
//Here will be the functionality.
}
}
window.customElements.define('viewer-lab', viewerLab);
Inside connectedCallback function we will create two things:
- shadowRoot: Is the shadowDOM of the component. It contains the styles of the component and its html definition.
- Conditions to apply certain functions to the component.
Filling the connectedCallback function:
class viewerLab extends HTMLElement {
//Step 1
//Step 2
//Step 3
//Step 4
//Step 5
//Step 6
//Step 7
connectedCallback () {
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
.modal {
z-index: 3000;
display: none;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.94);
font-family: Verdana,sans-serif;
display:flex;
justify-content:center;
align-items: center;
}
#info {
background-color:#2196F3;
left:88px;
font-size:18px;
text-align:center;
color:white;
margin-top:8px;
padding: 5px 16px;
}
#leftA {
position:absolute;
top:53%;
left:0%;
transform:translate(0%,-53%);
font-size:30px;
background-color: #3e3c3c99;
color:white;
}
#rightA {
position:absolute;
top:53%;
right:0%;
transform:translate(0%,-53%);
font-size:30px;
background-color: #3e3c3c99;
color:white;
}
.btn, .button {
border: none;
display: inline-block;
padding: 8px 16px;
vertical-align: middle;
overflow: hidden;
text-decoration: none;
color: inherit;
background-color: inherit;
text-align: center;
cursor: pointer;
white-space: nowrap;
}
.btn, .button {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.display-topright {
position: absolute;
right: 0;
top: 0;
}
.display-topleft {
position: absolute;
left: 20px;
top: 0;
font-size:25px;
text-align:center;
color:white;
margin-top:5px;
}
.container, .w3-panel {
padding: 0.01em 16px;
}
.image {
max-width: 100%;
height: auto;
}
img {
vertical-align: middle;
border-style: none;
}
@media (max-width:768px){
.modal{
padding-top:50px;
}
}
</style>
<div id="imageViewer" class="modal" >
<span id='closeBtn' class="button display-topright" style='color:white;font-size:30px;'>
×
</span>
<div id='counter' class='display-topleft' ></div>
<div id='info' class='display-topleft btn'>
Description
</div>
<div class="container">
<slot name='images'><span style='color:white;'>No images to show</span></slot>
<div id='leftA' class="button" >❮</div>
<div id='rightA' class="button" >❯</div>
</div>
</div>`;
shadowRoot.querySelector('#closeBtn').addEventListener('click', e => {
this.style.display = 'none';
});
if(this.noinfo){
shadowRoot.querySelector('#info').style.display='none';
}
//right arrow event
shadowRoot.querySelector('#rightA').addEventListener('click', e => {
this.nextImage();
});
//left arrow event
shadowRoot.querySelector('#leftA').addEventListener('click', e => {
this.prevImage();
});
//calling to populateChildren
this.populateChildren();
}
}
window.customElements.define('viewer-lab', viewerLab);
Check a working demo for this code: