TheRogerLAB Codes

Viewer web component

  July, 2021

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 2: Add a function to populate the children images

class viewerLab extends HTMLElement {
  //Step 1

  populateChildren(){
    if(this.children.length!=0){
      var ch = this.children;
      var cant = ch.length;
      for (var i = 0; i < cant; i++) {
        if(i == 0)
          ch[i].style.display = 'block';
        else
          ch[i].style.display = 'none';

        ch[i].style.margin = 'auto';
        ch[i].style.maxWidth = '100%';
        ch[i].style.maxHeight = '600px';
      }
      //counter
      this.shadowRoot.querySelector('#counter').innerHTML = '1/'+cant;
    } else {
      //hide the arrows
      this.shadowRoot.querySelector('#leftA').style.display = 'none';
      this.shadowRoot.querySelector('#rightA').style.display = 'none';
    }
  }
}
window.customElements.define('viewer-lab', viewerLab);

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:

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

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

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

Step 7: Add the functions for the arrows

class viewerLab extends HTMLElement {
  //Step 1

  //Step 2

  //Step 3

  //Step 4

  //Step 5

  //Step 6

  nextImage(){
    var ch = this.children;
    var cant = ch.length;
    for (var i = 0; i < cant; i++) {
      if(ch[i].style.display == 'block'){
        var actived = ch[0];
        var index = 0;
        if(i < (cant-1)){
          actived = ch[i + 1];
          index = i + 1;
        } 
      }
      ch[i].style.display = 'none';
    }
    actived.style.display = 'block';
    this.shadowRoot.querySelector('#counter').innerHTML = (index+1)+'/'+(cant);
  }
  prevImage(){
    var ch = this.children;
    var cant = ch.length;
    for (var i = 0; i < cant; i++) {
      if(ch[i].style.display == 'block'){
        var actived = ch[cant-1];
        var index = cant-1;
        if(i > 0){
          actived = ch[i - 1];
          index = i - 1;
        } 
      }
      ch[i].style.display = 'none';
    }
    actived.style.display = 'block';
    this.shadowRoot.querySelector('#counter').innerHTML = (index+1)+'/'+(cant); 
  }
}
window.customElements.define('viewer-lab', viewerLab);

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:

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(0000.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:

SEE DEMO RATE THIS ARTICLE
4.6
5
TheRogerLAB Codes
Powered by TheRogerLAB Sandbox

info@therogerlab.com