TheRogerLAB Codes

Image uploader web component

  June, 2021

Introduction

This component consists in an image uploader. Let's start by creating the structure of our web component. Create a file uploaderLab.js:

class uploaderLab extends HTMLElement {

  //Here will be all the functionality.

}
window.customElements.define('uploader-lab', uploaderLab);

Step 1: Add a constructor and variables

class uploaderLab extends HTMLElement {
  constructor () {
    super();
    this.data = [];
    this.fileName = [];
    this.deletedSrc = [];
    this.totalLoaded = 0;
    this.allowedExtension = ['image/jpeg','image/png'];
    this.thumb_height = '100';
    this.resize_width = '600';
    this.limitImage = 5;
  }
}
window.customElements.define('uploader-lab', uploaderLab);

In this case we create eight variables:

The deletedSrc array is used for edit purposes.

Step 2: Adding getters

class uploaderLab extends HTMLElement {
  //Step 1

  get btnSelectText() {
    return this.hasAttribute('btnSelectText');
  }
  get btnDeleteText() {
    return this.hasAttribute('btnDeleteText');
  }
  get thumbHeight() {
    return this.hasAttribute('thumbHeight');
  }
}
window.customElements.define('uploader-lab', uploaderLab);

In this case we create three attributes::

Step 3: Add a function to process the selected or dropped files

class uploaderLab extends HTMLElement {
  //Step 1

  //Step 2

  process(e,drop){

    //check if it is a selection or dropping
    if(drop == true)
      e.target.files = e.dataTransfer.files;

    //ckeck if there is invalid files
    var invalid = false;

    //is a valid extension?
    for (var b = 0; b < e.target.files.length; b++) {
      var el = this.allowedExtension.indexOf(e.target.files[b].type);
      if(el == -1){
        invalid = true;
        break
      }
    }

    //it is valid
    if(invalid == false){
      var width = this.resize_width;

      //the total lenght of the array if we add all files from input
      var realLength = this.fileName.length * 1 + e.target.files.length;
      var forLength = 0;
      if(realLength <= this.limitImage){
        //if it is valid we set the total with all files
        forLength = e.target.files.length;
      } else {
        //if it is not valid we set the total with -1 (add none)
        forLength = -1;
      }
      //if it is the firt time adding
      if(this.fileName.length == 0){
        //if it's greater than the limit we take only the allowed length
        if(realLength > this.limitImage)
          forLength = e.target.files.length - (e.target.files.length - this.limitImage);
      }

      //reading the files
      for (var i = 0; i < forLength; i++) {
        var reader = new FileReader();
        reader.readAsDataURL(e.target.files[i]);
        reader.name = e.target.files[i].name;
        reader.size = e.target.files[i].size;
        reader.totalToLoad = forLength;
        reader.cmp = this;
        reader.onload = function(event) {
          event.target.cmp.shadowRoot.querySelector('.loading').style.display = 'block';

          //creating a thumbnail
          var imgMin = document.createElement('img');
          imgMin.style.width = 'auto';
          imgMin.style.height = event.target.cmp.thumb_height + 'px';
          imgMin.src = event.target.result;
          imgMin.totalToLoad = event.target.totalToLoad;
          imgMin.cmp = event.target.cmp;
          imgMin.onload = function(e){
            var w = e.target.cmp.shadowRoot.querySelector('.total').innerHTML.split('/');
            e.target.cmp.shadowRoot.querySelector('.total').innerHTML = w[0] * 1 + 1 + '/' + e.target.cmp.limitImage;
            e.target.cmp.totalLoaded++;
            if(e.target.cmp.totalLoaded == e.target.totalToLoad){
              e.target.cmp.shadowRoot.querySelector('.loading').style.display = 'none';
              e.target.cmp.totalLoaded = 0;
            }

          };

          //the close button
          var titMin = document.createElement('span');
          titMin.setAttribute('class','closer');
          titMin.innerHTML = '×';
          titMin.cmp = event.target.cmp;
          titMin.onclick = function(e){
            var q = e.target.parentNode.parentNode.querySelectorAll('.closer');
            for (var x = 0; x < q.length; x++) {
              if(q[x] === e.target){
                var w = e.target.cmp.shadowRoot.querySelector('.total').innerHTML.split('/');
                e.target.cmp.shadowRoot.querySelector('.total').innerHTML = w[0] * 1 - 1 + '/' + e.target.cmp.limitImage;
                e.target.parentNode.parentNode.removeChild(e.target.parentNode);
                e.target.cmp.data.splice(x, 1);
                e.target.cmp.fileName.splice(x, 1);
              }
            }
          };

          //thumbnail container
          var imgCont = document.createElement('span');
          imgCont.setAttribute('class','card');
          imgCont.style.height = event.target.cmp.thumb_height * 1 + 10 + 'px';
          imgCont.style.marginRight = '5px';
          imgCont.style.cssFloat = 'left';
          imgCont.appendChild(imgMin);
          imgCont.appendChild(titMin);

          event.target.cmp.shadowRoot.querySelector('#cont').appendChild(imgCont);

          //creating image and then a canvas
          var img = new Image();
          img.src = event.target.result;
          img.name = event.target.name;
          img.size = event.target.size;
          img.cmp = event.target.cmp;
          img.onload = function(el) {
            var elem = document.createElement('canvas');
            var scaleFactor = width / el.target.width;
            elem.width = width;
            elem.height = el.target.height * scaleFactor;

            var ctx = elem.getContext('2d');
            ctx.drawImage(el.target00, elem.width, elem.height);

            var t = ctx.canvas.toDataURL('image/png'1);
            el.target.cmp.data.push(t);
            el.target.cmp.fileName.push(el.target.name); 
          }
        };
      }
    }
  }
}
window.customElements.define('uploader-lab', uploaderLab);

Step 4: Add a function to load images inside the component (for editing purposes)

class uploaderLab extends HTMLElement {
  //Step 1

  //Step 2

  //Step 3

  loadImages(){
    //clearall before load
    this.clearAll();
    var e = this.querySelectorAll('img');

    //setting a resized width
    var width = this.resize_width;

    //the total lenght of the array if we add all files
    var realLength = this.fileName.length * 1 + e.length;
    var forLength = 0;
    if(realLength <= this.limitImage){
      //if it is valid we set the total with all files
      forLength = e.length;
    } else {
      //if it is not valid we set the total with -1 (add none)
      forLength = -1;
    }
    //if it is the firt time adding
    if(this.fileName.length == 0){
      //if it's greater than the limit we take only the allowed length
      if(realLength > this.limitImage)
        forLength = e.length - (e.length - this.limitImage);
    }

    //the files
    for(var i = 0; i < forLength; i++) {
      this.shadowRoot.querySelector('.loading').style.display = 'block';

      //creating the thumbnail
      var imgMin = document.createElement('img');
      imgMin.style.width = 'auto';
      imgMin.style.height = this.thumb_height + 'px';
      imgMin.src = e[i].src
      imgMin.totalToLoad = forLength; 
      imgMin.cmp = this;
      imgMin.onload = function(e){
        var w = e.target.cmp.shadowRoot.querySelector('.total').innerHTML.split('/');
        e.target.cmp.shadowRoot.querySelector('.total').innerHTML = w[0] * 1 + 1 + '/' + e.target.cmp.limitImage;
        e.target.cmp.totalLoaded++;
        if(e.target.cmp.totalLoaded == e.target.totalToLoad){
          e.target.cmp.shadowRoot.querySelector('.loading').style.display = 'none';
          e.target.cmp.totalLoaded = 0;
        } 
      };

      //the close button
      var titMin = document.createElement('span');
      titMin.setAttribute('class','closer');
      titMin.setAttribute('data-old','true'); 
      titMin.innerHTML = '×';
      titMin.cmp = this;
      titMin.onclick = function(e){
        var q = e.target.parentNode.parentNode.querySelectorAll('.closer');
        for (var x = 0; x < q.length; x++) {
          if(q[x] === e.target){
            var w = e.target.cmp.shadowRoot.querySelector('.total').innerHTML.split('/');
            e.target.cmp.shadowRoot.querySelector('.total').innerHTML = w[0] * 1 - 1 + '/' + e.target.cmp.limitImage;
            e.target.parentNode.parentNode.removeChild(e.target.parentNode);
            e.target.cmp.data.splice(x, 1);
            e.target.cmp.fileName.splice(x, 1); 
            e.target.cmp.deletedSrc.push(e.target.previousSibling.getAttribute('src').split('/').pop());
          }
        }
      };

      //creating the thumbnail container
      var imgCont = document.createElement('span');
      imgCont.setAttribute('class','card');
      imgCont.style.height = this.thumb_height * 1 + 10 + 'px';
      imgCont.style.marginRight = '5px';
      imgCont.style.cssFloat = 'left';
      imgCont.appendChild(imgMin);
      imgCont.appendChild(titMin);

      this.shadowRoot.querySelector('#cont').appendChild(imgCont);

      //creating a image anf canvas
      var img = new Image();
      img.src = e[i].getAttribute('src');
      img.name = e[i].src.split('/').pop();
      img.cmp = this;
      img.onload = function(el) {
        var elem = document.createElement('canvas');
        var scaleFactor = width / el.target.width;
        elem.width = width;
        elem.height = el.target.height * scaleFactor;

        var ctx = elem.getContext('2d');
        ctx.drawImage(el.target00, elem.width, elem.height);
        var t = ctx.canvas.toDataURL('image/png'1);
        el.target.cmp.data.push('null');
        el.target.cmp.fileName.push('null');
      }
    } 
  }
}
window.customElements.define('uploader-lab', uploaderLab);

Step 5: Add function to deleteAll, clearAll and drop

class uploaderLab extends HTMLElement {
  //Step 1

  //Step 2

  //Step 3

  //Step 4

  deleteAll(){
    this.data = [];
    this.fileName = [];
    this.deletedSrc = [];
    this.totalLoaded = 0;
    this.shadowRoot.querySelector('.total').innerHTML = '';
    var q = this.shadowRoot.querySelector('#cont').querySelectorAll('[data-old="true"]');
    for (var x = 0; x < q.length; x++) {
      this.deletedSrc.push(q[x].previousSibling.getAttribute('src').split('/').pop());
    }
    this.shadowRoot.querySelector('#cont').innerHTML = '';
  }
  clearAll(){
    this.data = [];
    this.fileName = [];
    this.deletedSrc = [];
    this.totalLoaded = 0;
    this.shadowRoot.querySelector('.total').innerHTML = '';
    this.shadowRoot.querySelector('#cont').innerHTML = '';
  }
  allowDrop(ev,changeColor) {
    ev.preventDefault();
    if(changeColor == true)
      this.shadowRoot.querySelector('.component').style.border = '2px dashed #09d409';
    else
      this.shadowRoot.querySelector('.component').style.border = '2px dashed #c9c2c2';
  }
  drop(ev) {
    ev.preventDefault();
    this.shadowRoot.querySelector('.component').style.border = '2px dashed #c9c2c2';
    this.process(ev,true);
  }
}
window.customElements.define('uploader-lab', uploaderLab);

Step 6: Add a function connectedCallback

It fires when the component is created

class uploaderLab extends HTMLElement {
  //Step 1

  //Step 2

  //Step 3

  //Step 4

  //Step 5

  connectedCallback () {

    //Here will be the functionality.

  }
}
window.customElements.define('uploader-lab', uploaderLab);

Inside connectedCallback function we will create two things:

Filling the connectedCallback function:

class uploaderLab extends HTMLElement {
  //Step 1

  //Step 2

  //Step 3

  //Step 4

  //Step 5

  connectedCallback () {
    let shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <style>
        .closer{
          position:relative;
          background-color:black;
          padding: 2px 6px;
          box-shadow: 0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19);
          cursor:pointer;
          font-size: 13px;
          font-weight: 700;
          color:white;
          float: right;
          margin-left: -23px;
          display:none;
          font-family:'Verdana',sans-serif !important;
        }
        .btnSel{
          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;
          color: #fff !important;
          background-color: #2196F3 !important;
          border-radius: 32px;
          margin-top:5px;
          margin-left:5px;
        }
        .btnClear{
          border: 1px solid #e1e1e1;
          background-color: white !important;
          color: gray !important;
        }
        @-webkit-keyframes spin {
          0% { -webkit-transform: rotate(0deg); }
          100% { -webkit-transform: rotate(360deg); }
        }
        @keyframes spin {
          0% { transform: rotate(0deg); }
          100% { transform: rotate(360deg); }
        }
        .component{
          padding:5px;
          border:2px dashed #c9c2c2;
          text-align:center;
          overflow-y:auto;
        }
        #cont{
          margin-top: 15px;
          margin-left: 5px;
        }
        .card:hover span{
          display:block;
        }
        .total{
          color:gray;
          margin-top:10px;
          margin-bottom:10px; 
        }
        .loading {
          position: relative;
          margin-top:-10px;
        }
        .loading-bar {
          display: inline-block;
          width: 10px;
          height: 10px;
          border-radius: 100%;
          margin: 2px;
          animation: loading 0.6s ease-in-out infinite;
        }
        .loading-bar:nth-child(1) {
          background-color: #3498db;
          animation-delay: 0;
        }
        .loading-bar:nth-child(2) {
          background-color: #c0392b;
          animation-delay: 0.09s;
        }
        .loading-bar:nth-child(3) {
          background-color: #f1c40f;
          animation-delay: .18s;
        }
        .loading-bar:nth-child(4) {
          background-color: #27ae60;
          animation-delay: .27s;
        }
        @keyframes loading {
          0% {
            transform: scale(1);
          }
          20% {
            transform: scale(1.5);
          }
          40% {
            transform: scale(1);
          }
        }
      </style>
      <div class='component'>
        <div class='total'></div>
        <div id='loading' class="loading" style='display:none;'>
          <div class="loading-bar"></div>
          <div class="loading-bar"></div>
          <div class="loading-bar"></div>
          <div class="loading-bar"></div>
        </div>
      <div style='color:gray;margin-bottom:10px;'>
          <slot name='label'>Select or drop the images</slot>
      </div>

      <label id='btnSelect' class='btnSel' for='selectFile'>
        <slot name='btnSelect'>Select images</slot>
      </label>
      <label id='btnDelete' class='btnSel btnClear'>
        <slot name='btnDelete'>Delete all</slot>
      </label>

      <input style='display:none;' type="file" name='upload' id='selectFile' multiple>

      <div id='cont'></div>
      </div>`;

    //setting attributes and events
    if(this.btnSelectText)
      shadowRoot.querySelector('#btnSelect').innerHTML = this.getAttribute('btnSelectText');

    if(this.btnDeleteText)
      shadowRoot.querySelector('#btnDelete').innerHTML = this.getAttribute('btnDeleteText');

    if(this.thumbHeight)
      this.thumb_height = this.getAttribute('thumbHeight');

    shadowRoot.querySelector('input').addEventListener('change', e => {
      this.process(e,false);
    });
    shadowRoot.querySelector('#btnDelete').addEventListener('click', e => {
      this.deleteAll();
    });
    shadowRoot.addEventListener('drop', e => {
      this.drop(e);
    });
    shadowRoot.addEventListener('dragover', e => {
      this.allowDrop(e,true);
    });
    shadowRoot.addEventListener('dragleave', e => {
      this.allowDrop(e,false);
    });
  }
}
window.customElements.define('switch-lab', switchLab);

Database table example

Database table example

Sending data to the server (javascript).

function sendData() {
  var t = document.querySelector('#uploader');
  var fileName = t.fileName;
  var data = t.data;
  var deletedSrc = t.deletedSrc;

  //creating a string for the images name
  var filesArray = '';
  for (var i = 0; i < fileName.length; i++) {
    if (i == 0)
      filesArray = fileName[i];
    else
      filesArray = filesArray + '**' + fileName[i];
  }

  //creating a string for the images data
  var dataArray = '';
  for (var y = 0; y < data.length; y++) {
    if (y == 0)
      dataArray = data[y];
    else
      dataArray = dataArray + '**' + data[y];
  }

  //creating a string for the deleted images name
  var deletedArray = '';
  for (var z = 0; z < deletedSrc.length; z++) {
    if (z == 0)
      deletedArray = deletedSrc[z];
    else
      deletedArray = deletedArray + '**' + deletedSrc[z];
  }

  //sending to the server
  var fd = new FormData();
  fd.append("names", filesArray);
  fd.append("upload", dataArray);

  //always send a verificator(0 = adding and 1 = editing)
  fd.append("verificator"1);

  //deletedSrc and idrow only if you are editing
  fd.append("deletedSrc", deletedArray);
  fd.append("idrow"'1');//id row to edit

  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    //everything is ok
    if (xhr.readyState == 4 && xhr.status == 200) {
      var response = JSON.parse(xhr.responseText);
      if (response.success == true) {
        //if it was correct i clear all
        document.querySelector('#uploader').clearAll();
      }
    }
  }
  xhr.open("POST""upload.php");
  xhr.send(fd);
}

Receive the data (php)

<?php
//Creating a connection class
class connection{
  protected $connection;
  protected $servername = "localhost";
  protected $username = "root";
  protected $password = "password";
  public function conect(){
    $this->connection = new mysqli($this->servername,$this->username,$this->password);
    $this->connection->set_charset("utf8");
    return $this->connection;
  }
  public function disconect(){
    mysqli_close($this->connection);
  }
}
//end of connection class

$target_dir = "upload/";
$img = [];
$name = []; 
$toDelete = [];

$conex = new connection();
$connection = $conex->conect();

//if this case we are setting a name in server but usually comes in a post
$namePerson = 'John Doe';

/* In this case we will not use the files names
if($_POST['names']!='')
  $name = explode('**',$_POST['names']);
*/


//if verificator is 0 we will add
if($_POST['verificator'] == 0){
  $sql = sprintf("INSERT INTO myDatabase.myTable (`name`) VALUES ('%s')",
  mysqli_real_escape_string($connection,$namePerson)
  );
  $connection->query($sql);

  $id = $connection->insert_id;

  //if uploaded some images
  if($_POST['upload']!=''){
    $img = explode('**',$_POST['upload']);
    $nameString = '';
    for ($i = 0; $i < count($img); $i++) {
      if($img[$i]!='null'){
        $image = str_replace('data:image/png;base64,''', $img[$i]);
        $image = str_replace(' ''+', $image);
        $data = base64_decode($image); 
        $unique = uniqid();
        $file = $target_dir.$unique.'.png';
        $success = file_put_contents($file, $data);
        if($success != false)
          $nameString = $nameString.$unique.'.png;';
      }
    }
    if($nameString != ''){
      $sql1 = "UPDATE myDatabase.myTable SET images='".$nameString."'
      WHERE id='"
.$id."'";
      $connection->query($sql1);
    }
  }
else { //so if editing

  $id = $_POST['idrow'];

  //select the old images field
  $sql = "SELECT images FROM myDatabase.myTable WHERE id=".$id;
  $result = $connection->query($sql);

  $imagesOld = array();
  while($row = mysqli_fetch_assoc($result)){
    $imagesOld = $row['images'];
  }

  //if deleted some images
  if($_POST['deletedSrc']!=''){
    $toDelete = explode('**',$_POST['deletedSrc']);

    for ($y = 0; $y < count($toDelete); $y++) {
      //remove from folder
      unlink($target_dir.$toDelete[$y]);

      //remove from field string
      $imagesOld = str_replace($toDelete[$y].';'"", $imagesOld);
    }

  }

  //if uploaded some images
  if($_POST['upload']!=''){
    $img = explode('**',$_POST['upload']);
    for ($i = 0; $i < count($img); $i++) {
      if($img[$i]!='null'){
        $image = str_replace('data:image/png;base64,''', $img[$i]);
        $image = str_replace(' ''+', $image);
        $data = base64_decode($image);
        $unique = uniqid();
        $file = $target_dir.$unique.'.png';
        $success = file_put_contents($file, $data);
        if($success != false)
          $imagesOld = $imagesOld.$unique.'.png;';
      }
    }
  }

  //updating all
  $sql1 = "UPDATE myDatabase.myTable SET images='".$imagesOld."'
  WHERE id='"
.$id."'";
  $connection->query($sql1);
}

$conex->disconect();
echo '{"success": true}';
?> 

Check a working demo for this code:

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

info@therogerlab.com