Subir archivos usando Html5-XMLHttpRequest


http://www.developphp.com/video/JavaScript/File-Upload-Progress-Bar-Meter-Tutorial-Ajax-PHP

onreadystatechange::Responde a la solicitud realizada por el objeto http

Trabajar con ficheros en JavaScript consiste principalmente de:

  • Subir ficheros
  • Previsualizar ficheros en el DOM y
  • Descargar ficheros
Otras operaciones (como borrar) son propias del back-end y por ende no tiene mayor importancia que realizar una petición al servidor para que borre un fichero concreto.

Para la parte de la subida de ficheros, es importante entender cómo trabajan los navegadores y los servidores que siguen el estándar RFC-1867 (y sus posteriores revisiones). Puedes leer la sección teórica de subida de ficheros de PHP para entender esto: «Trabajando con ficheros en PHP«.

Apuntar directamente al formulario: esto serializará todos los inputs del formulario como «application/x-www-form-urlencoded» por defecto o bien como el tipo indicado en el atributo «enctype» del formulario.

  1. var formData = new FormData(document.getElementById('file_upload_form'));

Recoger los datos manualmente: para esto habrá que crear un objeto FormData vacío e ir metiéndole los datos con el método «append(<nombre_input>, <valor_input>)«. Nótese que para los inputs que no sean ficheros hay que añadirle el atributo «value» de dicho input; sin embargo, para los ficheros, hay que añadirle el atributo «files[i]» del input. El atributo «files» es un array que contiene los metadatos de cada uno de los ficheros que se han seleccionado en el input de tipo file. Podemos mandar todos los ficheros en una sola petición HTTP o bien mandar un fichero por cada petición HTTP (normalmente se mandan todos los ficheros juntos cuando la lógica de la operación requiere todos los ficheros a la vez, sino, lo ideal es mandarlos por separado ya que, en caso de fallo, el rollback de la grabación del fichero es mucho más sencillo).

IMPORTANTE: El formulario por defecto provocará que se navegue hacia la página donde se procesa la petición HTTP. Para evitar esto y conseguir que solamente se mande asíncronamente (mediante AJAX) hay que prevenir el evento por defecto («event.preventDefault()«).

  1. documnent.getElementById('file_upload_form').onsubmit = uploadFormOnSubmit;
  2. function uploadFormOnSubmit(event){
  3. // Cancelamos la operación por defecto
  4. event.preventDefault();
  5. // Crear el objeto FormData y mandarlo por AJAX
  6. }
Progreso de subida

Cuando subimos ficheros, es habitual querer saber qué porcentaje se ha subido en cada momento.

El objeto XMLHttpRequest tiene un atributo llamado «upload«, que no es más que un objeto que devuelve información del estado de la subida de los datos mandados a través de AJAX.

Entonces, para mantener una barra de progreso de la subida de los datos que queremos mandar al servidor, se deben añadir los siguientes «EventListeners=Escucha de eventos cuando load,error.porgress» al objeto «upload«:

  • progress: periódicamente se lanza este evento que indica la cantidad total de bytes que se ha transmitido al servidor.
  • load: cuando termina la subida de los datos pero antes de que se devuelva la respuesta de la petición HTTP.
  • error: cuando la subida ha fallado (motivo desconocido).
  • timeout: cuando la subida ha tardado tanto que se ha devuelto un estado de timeout.
  1. xhttp.upload.addEventListener("progress", function(){...});
  2. xhttp.upload.addEventListener("load", function(){...});
  3. xhttp.upload.addEventListener("error", function(){...});
  4. xhttp.upload.addEventListener("timeout", function(){...});

Para mantener actualizada una barra de progreso, en las funciones de cada evento, se debe manipular un nodo HTML de tipo «progress» de forma adecuada.


 Se pueden usar otras barras de progreso no estándar creadas mediante CSS, pero, para simplificarlo, vamos a usar la etiqueta «progress«.

Por ejemplo:

  1. xhttp.upload.addEventListener("progress", function(){
  2. // event.loaded indica cuánto se ha subido al servidor
  3. // event.total indica cuánto se está intentando enviar
  4. var pc = parseInt((event.loaded / event.total * 100));
  5. // progressBar = nodo <progress> de HTML
  6. progressBar.value = pc;
  7. // progressPercentaje = nodo de texto donde poner el porcentaje
  8. progressPercentaje.innerHTML = pc + '%';
  9. });

Cuando se reciba el evento «load«, deberíamos crear un loader infinito, esto es, un indicador de progreso cuyo tiempo y porcentaje de ejecución es indeterminado, ya que en este momento se está procesando la petición en el servidor y no podemos saber cuánto va a tardar en terminar.

Por ejemplo:

  1. // Si usamos font awesome tenemos un spinner loader
  2. // a modo de icono, fácil de meter en una misma línea.
  3. // En la misma línea donde ponía el porcentaje del progreso
  4. // podríamos poner algo como esto:
  5. // CSS:
  6. // Importar CSS de font awesome
  7. <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
  8. // JS:
  9. // Los datos están en el servidor pero aún no se ha recibido
  10. // respuesta HTTP
  11. xhttp.upload.addEventListener("load", function(){
  12. progressPercentaje.innerHTML = '<i class="fa fa-spinner fa-pulse"></i> Guardando...';
  13. });

Y finalmente, cuando la petición HTTP nos devuelva una respuesta, deberíamos indicar que el fichero (o ficheros, si los mandamos todos en una sola petición HTTP) ya se ha subido.

PREVISUALIZAR FICHEROS

Para ver los ficheros en el DOM del documento HTML, se deben crear los nodos adecuados apuntando a la URL del archivo (o del script del servidor que nos dé el fichero).

Es importante saber el tipo de fichero que se va a emplear para saber cómo tratarlo, pero esencialmente hay 3 tipos de ficheros: vídeo, audio e imagen:

  1. <!-- vídeo -->
  2. <video controls>
  3. <source src="URL_AL_FICHERO" type="MIME">
  4. </video>
  5. <!-- audio -->
  6. <audio controls>
  7. <source src="URL_AL_FICHERO" type="MIME">
  8. </audio>
  9. <!-- imagen -->
  10. <img src="URL_AL_FICHERO">

Donde pone URL_AL_FICHERO puede ser un acceso directo al fichero (por ejemplo: www.example.com/media/nombre_fichero.jpg) o bien puede ser un script del servidor que nos devolverá el fichero si estamos autorizados a visualizarlo (por ejemplo:

www.example.com / ajax / files / download.php ? file_name = nombre_fichero.jpg).

Donde pone MIME debemos indicar el tipo de objeto MIME que tiene el fichero (debería proporcionarlo el servidor en lugar de tener que calcularlo cada vez que se quiera visualizar el fichero).

Entonces, en JavaScript, crear la vista es tan sencillo como:

  1. function getFile(mime_type, file_name){
  2. function newNode(node){return document.createElement(node);}
  3. switch(mime_type){
  4. case 'video/mp4':
  5. case 'video/mkv':
  6. var video = this.newNode('video');
  7. video.setAttribute('controls', '');
  8. var source = this.newNode('source');
  9. source.setAttribute('src', file_name);
  10. source.setAttribute('type', mime_type);
  11. video.appendChild(source);
  12. return video;
  13. break;
  14. case 'audio/mpeg':
  15. case 'audio/wav':
  16. var audio = this.newNode('audio');
  17. audio.setAttribute('controls', '');
  18. var source = this.newNode('source');
  19. source.setAttribute('src', file_name);
  20. source.setAttribute('type', mime_type);
  21. audio.appendChild(source);
  22. return audio;
  23. break;
  24. case 'image/jpeg':
  25. case 'image/gif':
  26. case 'image/bmp':
  27. case 'image/png':
  28. var img = this.newNode('img');
  29. img.setAttribute('src', file_name);
  30. return img;
  31. break;
  32. default:
  33. console.warn('Tipo de fichero no controlado: '+mime_type);
  34. return null;
  35. break;
  36. }
  37. }

El objeto XMLHttpRequest

El objeto XMLHttpRequest ha sido actualizado varias veces desde que fue definido como parte del esfuerzo WHATWG’s HTML utilizando la tecnología de Microsoft; entonces tuvimos la especificación original XMLHttpRequest Level 1 como parte de la W3C, también tuvimos la especificación actualizada XMLHttpRequest Level 2, y ahora tenemos la últma version de este documento conocido como XMLHttpRequest Living Specification. Podemo resumir sus ventajas en los siguientes puntos:

  • Permite subir y bajar archivos como flujo de bytes (stream bytes), archivos binarios de gran tamaño (BLOBs) o formularios de datos.
  • Tiene manejadores de eventos de progreso, errores, aborto, comienzo, y fin de operaciones.
  • Peticiones inter dominio (cross-domain or CORS)
  • Nuevos tipos de respuestas para JSON
  • Es parte fundamental de la HTML5 File API specification
Es importante recalcar que antes de HTML5 y la nueva versión del objeto XMLHttpRequest se requería recurrir a tecnología de lado servidor para poder realizar una operación que permitiera subir un archivo, es decir no era posible subir un archivo nativamente del lado cliente. Tecnologías como AJAX y Flash hacían lo propio para tratar de hacer esto posible pero con serias limitaciones, por lo que XMLHttpRequest viene a cubrir este antiguo problema de gran manera. Existen otras características adicionales que acomapañan XMLHttpRequest Level 2 , si desea conocer más puede recurrir a la especificación oficial.



La mayoría de las veces utilizo código y comentarios en inglés, espero que esto no le resulte demasiado inconveniente, si tiene alguna duda la contestaré a la brevedad.
Comenzando


Lo primero que haremos es definir la interfaz de usuario para esta pequeña implementación comenzando por las etiquetas HTML, el código es muy sencillo y solo contempla algunos elementos de formulario, y algunos div que solo sirven para dar una mejor presentación auxiliandose de CSS3. No analizaré en este post lo que respecta a las hojas de estilo utilizadas ya que no es algo necesario para el funcionamiento del ejemplo.

<!DOCTYPE html>
<html>
<head>
<title>Upload File</title>
<meta charset="iso-8859-1" />
</head>
<body>
<div id="wrap">
<div class="field">
<ul class="options">
<li>
<input type="file" id="myfile" name="myfile" class="rm-input" onchange="selectedFile();"/></li>
<li>
<div id="fileSize"></div></li>
<li>
<div id="fileType"></div></li>
<li>
<input type="button" value="Subir Archivo" onClick="uploadFile()" class="rm-button" /></li>
</ul>
</div>
<progress id="progressBar" value="0" max="100" class="rm-progress"></progress>
<div id="percentageCalc"></div>
</div>
</body>
</html>

El código anterior se explica casi por si solo, pero resumamos lo que tiene:

  • Un input de tipo file que servirá para seleccionar el archivo que se desa subir.
  • Un div que servirá para imprimir el tamaño del archivo seleccionado.
  • Un div que servirá para imprimir el tipo MIME del archivo seleccionado.
  • Un botón que disparará el proceso para subir el archivo elegido.
  • Una barra que indicará el progreso en el proceso de subida del archivo (nuevo elemento HTML5).
  • Finalmente un div donde se mostrará el progreso en formato de porcentaje.

La función selectedFile()



Cada vez que selecciona un archivo con el elemento file, también dispara al evento onchange el cual llama a la función selectedFile(). En esta función suceden cosas interesantes, para empezar se hace referencia a una colección de archivos instanciada por un objeto nuevo en HTML5 llamado FileList, los objetos que obtenemos como miembros de las lista de FilesList son objetos File. En este caso obtendremos las propiedades size y type desde el objeto File recuperado.

Aprovenchando la información que proporciona la propiedad size, dentro de la función se calcula y se muestra en megabytes o kilobytes el tamaño del archivo que se ha seleccionado. Con la propiedad type se obtiene el tipo MIME del archivo seleccionado, el cual se muestra en el div correspondiente.


function FIEL_fileSelected(Obj) {

    FIEL_RestVal();
   
    var Arch1 = document.getElementById("ArchPDF").value.toUpperCase();
    var Ext1 = Arch1.substr(Arch1.length-4, 4);
   
    if (Ext1.length>0){
        if (Ext1!==".PDF"){
            alert("El archivo seleccionado no es válido");
            document.getElementById("ArchPDF").value = "";
            return false;
        }
    }
}

function selectedFile() {
var archivoSeleccionado = document.getElementById("myfile");
var file = archivoSeleccionado.files[0];
if (file) {
var fileSize = 0;
if (file.size > 1048576)
fileSize = (Math.round(file.size * 100 / 1048576) / 100).toString() + ' MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + ' Kb';
var divfileSize = document.getElementById('fileSize');
var divfileType = document.getElementById('fileType');
divfileSize.innerHTML = 'Tamaño: ' + fileSize;
divfileType.innerHTML = 'Tipo: ' + file.type;
}
}

La función uploadFile()

Esta es la función que hace un mayor uso de las nuevas posibilidades de XMLHttpRequest , y es la que se encargará de disparar el proceso principal del lado cliente.
function uploadFile(){
//var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile";
var url = "/ReadMoveWebSite/UploadMinimal.aspx";
var archivoSeleccionado = document.getElementById("myfile");
var file = archivoSeleccionado.files[0];
var fd = new FormData();
fd.append("archivo", file);
var xmlHTTP = new XMLHttpRequest();
//xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);
xmlHTTP.upload.addEventListener("progress", progressFunction, false);
xmlHTTP.addEventListener("load", transferCompleteFunction, false);
xmlHTTP.addEventListener("error", uploadFailed, false);
xmlHTTP.addEventListener("abort", uploadCanceled, false);
xmlHTTP.open("POST", url, true);
//xmlHTTP.setRequestHeader('book_id','10');
xmlHTTP.send(fd);
}

function FIEL_startUploading() {

   
   
    // get form data for POSTing---obtener datos de formulario para POSTING
    var vFD = new FormData(document.getElementById('FIEL_upload_form'));

    // create XMLHttpRequest object, adding few event listeners, and POSTing our data
    //crear un objeto XMLHttpRequest, agregar pocos detectores de eventos y publicar nuestros datos
    var oXHR = new XMLHttpRequest();


    //Evento que se dispara cada vez que hay un avance en el proceso que sube el archivo.
    oXHR.upload.addEventListener('progress', FIEL_uploadProgress, false);

    // Evento que se dispara cuando la transferecia se completa.
    oXHR.addEventListener('load',  FIEL_uploadFinish, false);
    oXHR.addEventListener('error', FIEL_uploadError, false);
    oXHR.addEventListener('abort', FIEL_uploadAbort, false);
    oXHR.open('POST', 'UpLoad_PDF.php');
    oXHR.send(vFD);
}



Inicialmente tenemos una variable url que usaremos para indicar donde está la página o servicio web que recibirá la petición de esta página para hacer el proceso indicado en el servidor; enseguida tal y como se hizo en la funcion selectedFile() se hace referencia al objeto File miembro FileList obtenido.

En la cuarta línea hay algo novedoso y muy útil, me refiero al objeto FormData, este objeto permite instanciar vía JavaScript un formulario web, es decir, es como si usted colocara con etiquetas HTML un formulario, o bien puede hacer referencia a uno ya existente asignándolo a un objeto FormData. Sin duada esto es de gran ayuda ya que significa que ahora usted puede crear un formulario y alterar los valores que envía de manera dinámica. Para adjuntar valores a un formualrio instanciado o referenciado con FormData se utiliza el método append(archivo, objeto), de esta manera en la quinta línea se agrega nuestro objeto File con el nombre de archivo.

Este es el fragmento de la función que abarca lo planteado:

//var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile";
var url = "/ReadMoveWebSite/UploadMinimal.aspx";
var archivoSeleccionado = document.getElementById("myfile");
var file = archivoSeleccionado.files[0];
var fd = new FormData();
fd.append("archivo", file);

Manejadores de eventos

Continuando con el resto de la función, podemos observar que finalmente instancía el objeto XMLHttpRequest y se asigna a la variable xmlHTTP, enseguida procedemos a la siguiente novedad, me refiero a la posibilidad de crear los nuevos eventos que forman parte de XMLHttpRequest Level 2 gracias al objeto upload. Los eventes que se agregan en este caso son:
  • loadstart. Evento que se dispara cuando inicia el proceso para subir el archivo.
  • progress. Evento que se dispara cada vez que hay un avance en el proceso que sube el archivo.
  • load. Evento que se dispara cuando la transferecia se completa.
  • error. Se dispara si el proceso falla con error explícito.
  • abort. Se dispara si el usuario interrumpe o la conexión de interrumpe.

No son los único eventos disponibles, consulte la especificación oficial para mayor información.

Los manejadores de eventos se declaran en el siguiente código:

var xmlHTTP= new XMLHttpRequest();
//xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);
xmlHTTP.upload.addEventListener("progress", progressFunction, false);
xmlHTTP.addEventListener("load", transferCompleteFunction, false);
xmlHTTP.addEventListener("error", uploadFailed, false);
xmlHTTP.addEventListener("abort", uploadCanceled, false);

Las funciones que se llaman en cada evento son las siguientes:

function progressFunction(evt){
var progressBar = document.getElementById("progressBar");
var percentageDiv = document.getElementById("percentageCalc");
if (evt.lengthComputable) {
progressBar.max = evt.total;
progressBar.value = evt.loaded;
percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%";
}
}
function loadStartFunction(evt){
alert('Comenzando a subir el archivo');
}
function transferCompleteFunction(evt){
alert('Transferencia completa');
var progressBar = document.getElementById("progressBar");
var percentageDiv = document.getElementById("percentageCalc");
progressBar.value = 100;
percentageDiv.innerHTML = "100%";
}
function uploadFailed(evt) {
alert("Hubo un error al subir el archivo.");
}
function uploadCanceled(evt) {
alert("La operación se canceló o la conexión fue interrunpida.");
}

La función progressFunction es la que actualiza tanto la barra de estado como el porcentaje que indican de manera gráfica y numérica el avance del proceso, el resto de las funciones únicamente despliegan un mensaje apropiado para el caso.

Código comentado

Si ha observado el código presentado habrá notado algunas líneas comentadas, eso es debido a que este es el código base para hacer algo un poco más complejo, pero decidí dejar esas líneas porque pueden ser una referencia útil para alguien:

//var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile";

La línea anterior es una llamada a un servicio HTTP .Net en lugar de a una página.

//xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);

Esta línea llama a una función que muestra un mensaje cuando inicia el proceso, la cual comenté porque después de ejecuar varias veces el código me pareció molesto.

El código completo

Así luce la implementación completa del código, no describo el código CSS3 ya que es irrelevante en lo que respecta a la funcionalidad, pero comparto una imágen que muestra como se ve ejecutándose:

<!DOCTYPE html>
<html>
<head>
<title>Upload File</title>
<meta charset="iso-8859-1" />
<link rel="stylesheet" type="text/css" href="estilosUploadFile.css" />
<script type="text/javascript">
function selectedFile() {
var archivoSeleccionado = document.getElementById("myfile");
var file = archivoSeleccionado.files[0];
if (file) {
var fileSize = 0;
if (file.size > 1048576)
fileSize = (Math.round(file.size * 100 / 1048576) / 100).toString() + ' MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + ' Kb';
var divfileSize = document.getElementById('fileSize');
var divfileType = document.getElementById('fileType');
divfileSize.innerHTML = 'Tamaño: ' + fileSize;
divfileType.innerHTML = 'Tipo: ' + file.type;
}
}
function uploadFile(){
//var url = "http://localhost/ReadMoveWebServices/WSUploadFile.asmx?op=UploadFile";
var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile";
var archivoSeleccionado = document.getElementById("myfile");
var file = archivoSeleccionado.files[0];
var fd = new FormData();
fd.append("archivo", file);
var xmlHTTP= new XMLHttpRequest();
//xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);
xmlHTTP.upload.addEventListener("progress", progressFunction, false);
xmlHTTP.addEventListener("load", transferCompleteFunction, false);
xmlHTTP.addEventListener("error", uploadFailed, false);
xmlHTTP.addEventListener("abort", uploadCanceled, false);
xmlHTTP.open("POST", url, true);
//xmlHTTP.setRequestHeader('book_id','10');
xmlHTTP.send(fd);
}
function progressFunction(evt){
var progressBar = document.getElementById("progressBar");
var percentageDiv = document.getElementById("percentageCalc");
if (evt.lengthComputable) {
progressBar.max = evt.total;
progressBar.value = evt.loaded;
percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%";
}
}
function loadStartFunction(evt){
alert('Comenzando a subir el archivo');
}
function transferCompleteFunction(evt){
alert('Transferencia completa');
var progressBar = document.getElementById("progressBar");
var percentageDiv = document.getElementById("percentageCalc");
progressBar.value = 100;
percentageDiv.innerHTML = "100%";
}
function uploadFailed(evt) {
alert("Hubo un error al subir el archivo.");
}
function uploadCanceled(evt) {
alert("La operación se canceló o la conexión fue interrunpida.");
}
</script>
</head>
<body>
<div id="wrap">
<div class="field">
<ul class="options">
<li>
<input type="file" id="myfile" name="myfile" class="rm-input" onchange="selectedFile();"/>
</li>
<li>
<div id="fileSize"></div></li>
<li>
<div id="fileType"></div></li>
<li>
<input type="button" value="Subir Archivo" onClick="uploadFile()" class="rm-button" /></li>
</ul>
</div>
<progress id="progressBar" value="0" max="100" class="rm-progress"></progress>
<div id="percentageCalc"></div>
</div>
</body>
</html>

No descibo el CSS3 porque es irrelevante en términos de funcionalidad, pero comparto una imagen que muestra como luce la implementación en el navegador y un enlace al CSS3 estilosUploadFile.zip.

También comparto el HTTP service que utilicé para este ejemplo (el código de lado servidor, archivo de backend o cualquier otro nombre que ud. prefiera 😃) pero esto no será de mucha ayuda amenos que utilice exactemente el mismo stack que yo utilicé en su momento. En otras palabras, si usted es el tipo de personas que solo quiera copiar y pegar….jejeje bueno…quizá no está listo para hace esto todavía. Aquí está el tan solicitado archivo WSUploadFile.zip

Es todo por ahora amigos, espero que esto les sea de utilidad.

Algunos buenos libros que pueden ayudarle en su viaje por HTML5:


Subir archivos usando Html5

XMLHttpRequest tiene nuevos cambios en la especificación Html5, que especifica que la especificación XMLHttpRequest Nivel 2 (la última versión) contiene las siguientes características nuevas:


Maneje secuencias de bytes, como archivos, blobs y objetos FormData para cargar o descargar eventos de progreso durante la carga o descarga Solicitudes entre sitios Permitir la creación de solicitudes anónimas Puede establecer tiempos de espera de solicitudes



https://www.youtube.com/watch?v=1kA4DowJs08&t=49s

https://www.youtube.com/watch?v=TF-fwphqt7g


#2 AJAX con JavaScrip

https://www.youtube.com/watch?v=M4LaQ3KUGOM&list=PLPl81lqbj-4L6JypMCzyMqZ0v87dltjqD&index=3

#3 AJAX con JavaScript

https://www.youtube.com/watch?v=M3w4S94Vl7Q&list=PLPl81lqbj-4L6JypMCzyMqZ0v87dltjqD&index=4

Crear el objeto XMLHttpRequest 

https://www.youtube.com/watch?v=TF-fwphqt7g


AJAX FÁCIL y SIN FRAMEWORKS [ 01 de 05 

https://www.youtube.com/watch?v=TF-fwphqt7g

https://www.youtube.com/watch?v=GXMNfstLBOI

AJAX FÁCIL y SIN FRAMEWORKS [ 02 de 05 ]

https://www.youtube.com/watch?v=j_RiqixHUnw&t=1s

AJAX FÁCIL y SIN FRAMEWORKS [ 03 de 05


https://www.youtube.com/watch?v=elVi0T_O3eM&t=36s

Qué es XMLHttpRequest en AJAX y como funciona

https://www.youtube.com/watch?v=nz9S3uZE_dM&t=183s

Estados y Propiedades en XMLHttpRequest

https://www.youtube.com/watch?v=7kyA-4hCvoc

AJAX FÁCIL y SIN FRAMEWORKS [ 04 de 05 ] -archivo

https://www.youtube.com/watch?v=0qhvOK7jQpE

#1 AJAX con Vanilla JavaScript - XMLHttpRequest |

https://www.youtube.com/watch?v=1kA4DowJs08&list=PLPl81lqbj-4L6JypMCzyMqZ0v87dltjqD

Enviar datos por método POST a PHP con Formulario HTML/JAVASCRIPT 

 https://www.youtube.com/watch?v=nLrL9Ip3tWI&t=1s

Comentarios

Entradas populares de este blog

Filtrando por fecha

10 videojuegos gratis para aprender JavaScript en línea

reloj obs---datetime.lua