005. Uploader con Ajax (3)

En este capítulo vamos a hacer que nuestro cargador de archivos sea DRAG AND DROP, para lo que vamos a tener que hacer ciertos cambios en los elementos HTML. Vamos a trabajar con el API DRAG AND DROP, donde podemos encontrar toda la información de la misma en MDN.

Nosotros a cualquier elemento en el HTML le podemos dotar de las características de DRAG AND DROP. En este capítulo no utilizaremos la delegación de eventos porque el único elemento que va a ser zona de interacción activa va a ser la drop-zone, por lo que los eventos se los vamos a asignar directamente a la drop-zone.

Hay varios eventos de DRAG AND DROP, en este capítulo utilizaremos 3:

  • dragover: el evento cuando estamos sobre el elemento. Es el evento que se va a estar ejecutando mientras tengamos un archivo sobre la zona que es DRAG AND DROP.
  • dragleave (es como el mouseout): el evento cuando sales de la zona arrastrable y soltable.
  • drop: el evento cuando soltamos el archivo. Es realmente el evento que hace que los archivos suban al servidor.

Unicamente vamos a modificar el archivo uploader.html, al que le vamos a llamar ahora uploader-drag-and-drop.html, cuya sintaxis es la siguiente.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Uploader Drag and Drop con AJAX</title>
    <style>
      html {
        box-sizing: border-box;
        font-family: sans-serif;
        font-size: 16px;
      }

      *,
      *::after,
      *::before {
        box-sizing: inherit;
      }

      body {
        margin: 0;
      }

      main {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        text-align: center;
        min-height: 100vh;
      }

      progress {
        margin-top: 1rem;
      }

      .drop-zone {
        margin-left: auto;
        margin-right: auto;
        border: thin dotted #000;
        width: 80%;
        height: 40vh;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
      }

      .drop-zone.is-active {
        border: thick dashed #000;
        background-color: rgba(0, 128, 0, 0.5);
        animation: pulse 1.5s infinite;
        animation-timing-function: linear;
      }

      @keyframes pulse {
        0%,
        100% {
          transform: scale(1);
        }
        50% {
          transform: scale(1.1);
        }
      }
    </style>
  </head>

  <body>
    <main>
      <article class="drop-zone">
        <p>Arrastra y suelta tus archivos...</p>
      </article>
    </main>

    <script>
      const d = document,
        $main = d.querySelector("main"),
        $dropZone = d.querySelector(".drop-zone");
      // Creamos función que mediante AJAX permita subir la carga de archivos
      // Esta función se ejecutará por cada archivo que reciba el input

      const uploader = (file) => {
        const xhr = new XMLHttpRequest(),
          formData = new FormData();
        formData.append("file", file);

        xhr.addEventListener("readystatechange", (e) => {
          if (xhr.readyState !== 4) return;

          if (xhr.status >= 200 && xhr.status < 300) {
            let json = JSON.parse(xhr.responseText);
            console.log(json);
          } else {
            let message = xhr.statusText || "Ocurrió un error";
            console.log(`Error ${xhr.status}: ${message}`);
          }
        });

        xhr.open("POST", "uploader.php");
        xhr.setRequestHeader("enc-type", "multipart/form-data");
        xhr.send(formData);
      };

      const progressUpload = (file) => {
        const $progress = d.createElement("progress"),
          $span = d.createElement("span");
        $progress.value = 0;
        $progress.max = 100;
        $main.insertAdjacentElement("beforeend", $progress);
        $main.insertAdjacentElement("beforeend", $span);
        const fileReader = new FileReader();
        fileReader.readAsDataURL(file);

        fileReader.addEventListener("progress", (e) => {
          let progress = parseInt((e.loaded * 100) / e.total);
          $progress.value = progress;
          $span.innerHTML = `<b>${file.name} - ${progress}%</b>`;
        });

        fileReader.addEventListener("loadend", (e) => {
          uploader(file);
          setTimeout(() => {
            $main.removeChild($progress);
            $main.removeChild($span);
          }, 3000);
        });
      };

      $dropZone.addEventListener("dragover", (e) => {
        e.preventDefault();
        e.stopPropagation();
        e.target.classList.add("is-active");
      });

      $dropZone.addEventListener("dragleave", (e) => {
        e.preventDefault();
        e.stopPropagation();
        e.target.classList.remove("is-active");
      });

      $dropZone.addEventListener("drop", (e) => {
        e.preventDefault();
        e.stopPropagation();
        const files = Array.from(e.dataTransfer.files);
        files.forEach((el) => progressUpload(el));
        e.target.classList.remove("is-active");
      });
    </script>
  </body>
</html>

El ejercicio completo está en el siguiente ZIP:

Scroll al inicio