duduromeroa.com

#Javascript, #DOMJs, #webinteractiva, #Interfaces, #duduromeroa, #Guayaquil

Javascript | DOM | API

Programación para web (sec. 1.6): Métodos y propiedades para alterar nodos y elementos HTML mediante JS DOM.


George Póyla, matemático. Resolver problemas de diseño. www.duduromeroa.com

Por Eduardo J. Romero Andrade
| Guayaquil

Contacto duduromeroa@gmail.com

Aclaraciones
Dirijo este contenido para quienes ya cuentan con comprensión previa en HTML, CSS y JS. Para ampliar conceptos se propone una bibliografía en inglés al final de esta página.


Para resaltar algunos elementos de la sintaxis de JS, en algunos ejemplos de código se agregaron espacios entre caracteres. La sintaxis correcta NO usa esos espacios.


En algunos ejemplos expongo código y comentarios. También revisa el código fuente (click derecho desde mouse > Page Source). Allí también comento los códigos.






MÉTODOS Y PROPIEDADES PARA ALTERAR NODOS Y ELEMENTOS

Algunos métodos y propiedades que alteran o agregan interacción en nodos y elementos:

  • createElement("tag")
  • appendChild("nodo")
  • removeChild()
  • --
  • innerHTML
  • createTextNode()
  • --
  • classList y métodos:
    • add(className)
    • remove(className)
    • toggle(className)
    • contains(className)
    • item(numeroIndice)
    • replace(oldClass, newClass)
  • cloneNode()
  • --
  • textContent
  • innerText

MÉTODO bodyAppend()

Adjunta un elemento al último nodo hijo del cuerpo de la página web. En el ejercicio siguiente y luego de cliquear el botón, un nuevo elemento será mostado al final de esta página, es decir, después del último elemento contenido en esta página.

Mostrar


Cuidado con la sintaxis. Si deseamos alterar un elemento desde Javascript, debemos invocar correctamente el método.

En Javascript

 // INCORRECTO
botonciito.backgroundColor = "peru";

// CORRECTO (el método es style)
botonciito.style.backgroundColor = "peru";

MÉTODO createElement()

Crea un nuevo elemento según la etiqueta explicitada; y mediante la propiedad 'textContent' se le inserta el contenido a mostrar. Así mismo, mediante 'elementoP.classList.add("nombreClase");



MÉTODO nodoPadre.appendChild(nodoHijo)

Añade un nodo al final de un elemento hijo contenido a su vez dentro de un elemento padre especificado. En el parámetro se agrega el nodo a ser agregado. Además que devuelve un único nodo insertado (solo uno).

Tener en cuenta que el agregado del nodo hijo será iterativo solo si la creación del elemento nodo está dentro de la función .

La capital del Guayas es



'padre . appendChild (hijo);' funciona bien con elementos texto. pero ¿Qué si deseamos agregar imágenes?:

  • Recordar: Para apuntar a un elemento mediante document . querySelector (" ") debemos agregar el nombre del elemento pero también su signo identificador. Ejemplo:
  • document . querySelector (".nombreClase")
  • document . querySelector ("#nombreId")
  • document . querySelector ("nombreId") ERROR!


MÉTODO append()

Método nuevo dado en la actualización del DOM de junio del 2023. Inserta un objeto string, un nodo o un conjunto de nodos (o colección de elementos HTML) después del último hijo del elemento padre.

El elemento padre está ubicado a la izquierda de append(). Y si luego hay un segundo elemento en la estructura HTML, entonces ese será el elemento hijo. Luego de ese, append() insertará. El método append() acepta múltiples argumentos; pero no devuelve el objeto (como sí lo hace appendChild (nodoHijo)).

La diferencia entre 'conjuntos de objetos nodos' y 'objetos string' es:

  • Conjunto de objetos nodos: es una colección o conjunto de elementos que son apuntados mediante un bucle de índices; pero que no pueden ser alterados por otras funciones como push().

  • Objeto string: es una secuencia de caracteres agrupados entre comillas. Por ejemplo: "este es un objeto string"

Así, en el ejemplo inferior, luego del elemento padre se insertaron dos nuevos elementos HTML (puesto que no hay hijos):

  • elementoPadre . append ( hijo1, hijo2 );

¿Qué tiene de bueno Guayaquil?



  • Diferencia entre append() y appendChild()
  • appendChild(hijo) añade un solo 'niño' o hijo
  • append(hijo1,hijo2) añade más de un elemento luego del hijo

MÉTODO removeChild()

Borra un nodo hijo y lo devuelve. En este contexto, 'devolver' significa mantener una referencia (o lo que es lo mismo, mantener el acceso) a ese nodo desde la memoria para, en breve, alojarlo en una variable, operarlo, alterarlo o combinarlo en otra operación dentro del mismo contexto.

Se recalca que el nodo devuelto podrá será accesible desde memoria, pero no aparecerá en el DOM a menos que, una vez alojado en una variable, se le pida mediante código que sea mostrado en el sito web.

En el ejemplo de abajo, en cada ejecución desde el botón se eliminará el primer nodo hijo (no aparecerá en el DOM). Luego, se alojará en una variable el llamado al nodo eliminado y luego, recuperado, se lo alterará y mostrará. En este contexto, cada ejecución desde el botón hará la misma acción (eliminar el hijo de índice cero y devolverlo en color rojo.

Elemento hijo primero

Elemento hijo segundo


Así mismo, se puede eliminar un elemento hijo al conocer cuál es el elemento padre:

  • Atentos a la redacción de las palabras clave. Por ejemplo, esta función nunca funcionará por una pequeña razón:
  • functon eliminar(){ // aquí la operación }
  • ¿Identificaste el error? lo correcto es function

Padre y sus hijos celestes

Hijo 1
Hijo 2
Hijo 3

Tomar en cuenta detalles como este: el texto del botón debe alertar al usuario que no hay más nodos que borrar.

En Javascript

function eliminarHijos() {
// Aloja y apunta el elemento padre mediante ID
let padre = document.getElementById("padre");

// Aloja y apunta a los elementos con la misma clase hijo 
let hijo = padre.querySelector(".elemHijo");

/* Condicional
- Si primerHijo es true (es decir, habemus un elemento con clase tal)...
- borrar el primero nodo desde lo obtenido en 'apuntarAlpadre'.
*/ 
    if (hijo) {
    // Ejecutar borrado de hijo del padre
    padre.removeChild(hijo);

    // Sigue secuencia en true
    /* Luego entramos a otro If */
    /* SI [ al apuntar a los elementos con la clase
     '.hijodDelPadre' resulta en null ] DA TRUE */

        /* Ojo, la igualdad debe ser estricta */
        // Si ya no hay elementos hijos
        if (padre.querySelector(".elemHijo") === null) {

        // Si da TRUE, sigue la secuencia
        // Alterar elemento txt del boton
        document.getElementById("boton").
        innerHTML = "Todos los hijos borrados";

    document.getElementById("boton").
        style.backgroundColor = "#FF1493";
        }
    }
}



PROPIEDAD style.display

Como ya hemos visto, esta importante propiedad style accede a un valor de los atributos del objeto CSS (HTML DOM Style Object). Este objeto contiene todas las propiedades de estilo CSS que pueden ser accedidas desde JS. A junio del 2023 son 188 propiedades (que no tiene la misma sintaxis que en las reglas CSS) y están enlistadas de A a Z en el del W3Schools.

La propiedad style admite los valores de atajo como "border-top: 1px solid black;". Y los valores pueden ser 'limpiados' mediante null, como en elm.style.color = null;

Textito a manipular


Así mismo, para reiterar el desplazamiento del texto y que el botón lo ejecute cada vez que se lo presione, se debe alterar el nombre de la animación y así poder ejecutarla cada vez que la función sea activada mediante botón.

Textito a manipular





PROPIEDAD elemento.innerHTML

Define el contenido interior (inner) de un elemento HTML, reemplazándolo por un nuevo definido por string, excepto que no sean nodos HTML comunes. innerHTML también captura el contenido del elemento dado.

Ya que se capta el contenido del elemento A, cada alteración de ese contenido se mostrará en la captura (y en la exposición de ese contenido en la página).

Sin embargo, hay una alerta: mediante un campo de texto para usuarios externos, alguien podría usar innerHTML para insertar código dañino. Por lo que en campos de texto abiertos se recomienda usar el método . Este limpia de sintaxis potencialmente dañinas a los ingresos de texto.

Elemento A) Contiene 'soy duduromeroa.com'



MÉTODO createTextNode()

Crea un nuevo nodo de texto, con los caracteres o strings dados en el parámetro. Un nodo de texto es también un objeto, con propiedades y métodos, susceptible el nodo de ser alterado. Así mismo, un nodo de texto es uno o más espacios de caracter vacíos, que suele usarse para iniciar una nueva línea de código en cada presión de tecla enter↵.

En el ejemplo de abajo, se inicia con el cuerpo de la página vacía (con solo un botón de activación de la función y con un contenedor div vacio); y el código insertará el resto mediante:

  • document . createElement ("tagHTML");:: Crea nuevo nodo HTML
  • document . createTextNode :: crea nuevo nodo de texto
  • appendChild() :: añade un nodo al final de un nodo hijo

Y con un if/else y condicionales ternarios para alternar el despliegue del botón (abrir/ cerrar):

Condicionales ternarios:

En Javascript

/* Los ternarios inician con una condición cuyo valor 
true activa el primer operador; o el valor false, 
que activa el segundo operador */
function valor(booleano) {
  return (booleano ? 'primero, Verdad' : 'segundo, Falso');
}

console.log(valor(true));   // "primero, Verdad"
console.log(valor(false));  // "segundo, Falso"
console.log(valor(null));   // "segundo, Falso"

En el ejemplo de abajo se usó los ternarios para que el código decida según true o false, que valor agregar a la propiedad CSS:

En Javascript

// Se aloja en variable 
elemento1.innerHTML = elemento2.style.display === 
"none" ? "true. muestra esto" : "false. muestra esto";



PROPIEDAD classList

Devuelve una colección de atributos clase de un elemento. Esas clases pueden ser usadas para manipular elementos.

className

En Javascript

// Se crea elemento HTML con tag 'div'
var elemento = document.createElement("div");

/* Se agrega una clase al nuevo elemento al acceder 
desde el elemento a la propiedad 'className' */
elemento.className = "elementoEscoger"

// SE EVALUA
console.log(elemento);
// Muestra: <div class="elementoEscoger"></div>

console.log(elemento.classList.contains("elementoEscoger"));
// Muestra: True  

classList.remove()

También podemos quitar clases y reponerlas. Quitamos clases con el método classList. remove("clase–A-Quitar"); y agregamos clases con classList. add("clase–A-Agregar");

En el siguiente ejemplo agregamos un nombre para una clase (mediante .className), pero luego quitamos ese nombre de clase mediante .classList.remove("").

En Javascript

// Se crea elemento HTML con tag 'div'
var elemento = document.createElement("p");

/* Se agrega el nombre de clase al elemento */
elemento.className = "cosito1"

// Pero luego quitamos la clase
elemento.classList.remove("cosito1");

// Se evalua si esa clase (que eliminamos) aún existe
console.log(elemento.classList.contains("cosito1"));
// Muestra: False 
// [ no existe, pues la eliminamos ]

classList.add()

Método con el que podemos agregar un nuevo nombre de clase. Cuidado con usarlo como una propiedad a la que debemos agregar un valor (se dio más espacios en la sintaxis para resaltar sus elementos):

  • Error!: elemento. classList. add = "oficina";
  • Correcto: elemento. classList. add("oficina");

En Javascript

/* Se aloja y se apunta a elemento HTML 
para captar el nombre de la clase */
var elemento = document.querySelector(".trabajo");

// Se remueve la clase
elemento.classList.remove("trabajo");

// Se actualiza para agregar nueva clase
elemento.classList.add("oficina");

// Se evalua si esa clase existe en el elemento
console.log(elemento.classList.contains("oficina"));
// true

console.log(elemento.classList.contains("trabajo"));
// false

Crear e insertar un elemento HTML que no existía y agregarle estilos. Uso de document.createElement('item') y classList.add('item');

  • MÉTODOS PARA: Crear nuevo elemento HTML, añadirle un nombre de clase, e insertarlo desde un lugar dado:
  • document.createElement('div');
  • elem.classList.add('nombreClase');
  • elem.insertAdjacentElement('afterend', item);

En el ejemplo de abajo creamos un elemento que no existe previamente en la estructura HTML, y le agregaremos una clase con estilos. En este caso, aquí deseamos que el nuevo elemento creado sea mostrado después de un elemento dado por nosotros. El método elem. insertAdjacentElement ('afterend', item); será finalmente el responsable de mostrar en el sitio web el nuevo elemento con sus estilos de la clase agregada:

Tocar y mostrar



En otro ejemplo, el nombre de la clase (sin CSS) de un elemento ya presente en la estructura HTML es reemplazada (o añadida) por un nuevo nombre de clase que contiene nuevas propiedades CSS:

Holi!
Click aquí y cambiar


  • INSERTAR UN ELEMENTO LUEGO DE OTRO
  • El método insertAdjacentElement('afterend', item); permite insertar un elemento después o antes que otro.

classList.toggle()

Intercambia entre dos estados de una clase: estado 1: eliminar la clase (si está). Estado 2, agregar la clase (si no está). Solo acepta un nombre de clase en el parámetro.

  • OJO: Es toggle(), y no 'togle()', o 'toggle()', o 'tooglee()', o toglee().

En este ejercicio se pide intercambiar la clase de un elemento (con sus reglas CSS) por otra clase (con otras reglas CSS). Sin embargo, se evidencia que no todos los estilos se activan en el intercambio de clases: la primera fuente es 'Courier', mientras que la nueva clase debería mostrar 'Garamond'.

'Eres perla que surjistee..'


La causa de eso son las reglas de precedencia CSS, preferencia o reelevancia en los estilos aplicados a cada elemento HTML.

  • REGLAS DE PRECEDENCIA CSS
  • Evalua selectores id, class y tipo para determinar cuál debe predominar cuando hay más de un estilo aplicado en un solo elemento. Los niveles de reelevancia de mayor a menor son las siguientes:
    • Estilos agregados directamente en la línea del código HTML tienen alta precedencia y sobreescriben otros estilos. Sin embargo, no se recomienda su uso por lo difícil de ubicar y actualizar.
    • Selectores 'id' tienen más precedencia que los selectores 'class'.
    • Selectores 'class', selectores con atributo [class="valor"] y pseudo clases como a:hover{} tiene similar peso de precedencia.
    • Selectores de elementos HTML como p{estilo}; o div{estilo} tienen mínima precedencia.
    • A más lejano el selector de su elemento, más reelevancia de ese selector por sobre otros. Es decir, el último selector gana.

Según eso, cuando se intentó intercambiar mediante classList.toggle() el estilo 'courier' (del id) por el estilo 'Garamond' (de class) pesó más la regla CSS del selector id. Es decir, el estilo agrupado en la clase no reescribió el estilo agrupado en el id; pues los estilos agrupandos en un id tienen mayor peso. Eso se corrigió cambiando el selector id por el de class.

'Eres perla que surgistee..'



En Javascript

function intercambia(){
// Aloja el elemento captado por su clase
var elem1 = document.querySelector(".claseInicial");
// Se intercambia el nombre de su clase por otra clase
elem1.classList.toggle("nuevaClase");
}

/* EN CSS
- .claseInicial{}; tiene unos estilos CSS
- .nuevaClase{}; tiene otros estilos CSS
*/

classList.contains()

Da booleano (true/false) si hay presencia o no del nombre de una clase.

En HTML

<div class="claseHoli"> ... </div>

En Javascript

var apuntar = document.querySelector(".claseHoli");
console.log(apuntar.classList.contains("claseHoli"));
// True

console.log(apuntar.classList.contains("holiBoli"));
// False

Método classList.item(numeroIndice)

Accede al índice numérico que apunta al identificador de una clase. El nombre de esa clase suele ser parte de un conjunto de clases alineadas según índices del 0 en adelante. Una vez apuntado al nombre de la clase desde su índice, se retorna el nombre (solo el nombre, NO el elemento) de la clase.

Es decir, classList. item( numeroIndice ); solo accede al string/nombre de una clase mediante su ubicación índice, aunque forme parte de un conjunto de clases. Recordar que los elementos HTML sí pueden tener más de una clase, PERO solo pueden tener un solo identificador ID.

En HTML

<!--- Elemento HTML con identificador ID. 
Inicialmente debe tener un ID para poder 
ser apuntado desde JS -->
<div id="zzz"> hola </div>

En Javascript

// Guarda y apunta a un ID de nombre 'zzz' (minúsculas)
const apuntarAelemento = document.getElementById('zzz');

// elemento.classList.add('--', '--')
/* Añade múltiples clases al elemento apuntado arriba. 
Se agregan las clases en un conjunto de clases 
indexadas desde del cero en adelante */
// ---------------------------->Indice0---indice1---indice2
apuntarAelemento.classList.add('claseAgua', 'clasePato', 'claseSol');

// elemento.classList.item(numeroIndice);
// Aloja y apunta dentro del conjunto de clases a la clase índice '1' 
const nombreClaseBuscada = apuntarAelemento.classList.item(1);

// Se invoca a la clase apuntada y alojada
console.log(nombreClaseBuscada);
// Output: class2

console.log(apuntarAelemento.id);
// Muestra el ID 'zzzz'

// OJO propiedad className
console.log(apuntarAelemento.className);
// Muestra todas sus clases: class1 class2 class3

console.log(apuntarAelemento.classList.item(1)); 
// Muestra solo la clase del índice 1: 'class2'

/* Ahora el elemento retiene su ID y también sus 3 nuevas clases */
// <div id="zzz" class="class2 class2 class3"> hola </div>

RECORDAR

  • No es pecado que un elemento HTML contenga solo un ID y varias CLASES:
  • Por ejemplo: <p id="aa" class="bb cc dd"> ... </p>
  • Sí es pecado, en cambio, que más de un elemento HTML contenga el mismo ID. Eso significa hoguera🔥🚨

Sin embargo, se debe recordar que element. classList. item(numIndice) solo obtiene el nombre de la clase. NO al elemento que apunta a esa clase. Por lo que si deseamos obtener un elemento HTML (clase o Id) debemos recurrir a document. querySelector(". nombreClase"); o document.querySelector("#nombreId");

El siguiente obtiene el nombre de la clase y la muestra. Se agrega una sencilla animación desde CSS pero invocada desde JS.

Elemento con clases varias
Eso mostrará el nombre de clase....


Una forma de volver al estado inicial (antes de mostrar el nombre de la clase) es mediante la función setTimeout()(lo estudiaremos luego). Este método funciona como un temporizador que ejecuta código después de pasado un tiempo explícito en el código.

Elemento con clases varias
Eso mostrará el nombre de clase....


Finalmente, un ejemplo con una condicional if/else simple, para cambiar de un estado a otro del botón.

Elemento con clases varias
Eso mostrará el nombre de clase....


Método replace(claseAnterior, claseNueva)

Reemplaza un 'nombre de clase' anterior por uno nuevo (o en contexto, un token o mínima unidad de un dato). Se reemplaza una clase contenida en una lista de clases y lo cambia con el token de una clase nueva. Si el token no existe, se retorna false sin añadir nada.

Considero al método replace (claseAnterior, claseNueva) como fantástico. Pues permite intercambiar clases (que contengan, por ejemplo, reglas CSS) para crear activaciones visuales.

En el ejemplo de abajo: la clase inicial del elemento que contiene el texto 'Titulazo' contiene reglas CSS sencillas (apenas el gris en la tipografía 'Arial').

Pero intercambiar esa clase por otra que contiene reglas CSS de color y animación (la clase 'titulazoNuevo') significa que añadimos una alteración explícita desde las reglas CSS de la nueva clase. Finalmente, se aplica setTimeout(function(){...} dentro de la función que activa el botón para reiniciar los estados.

Titulazo



Aunque el método replace ("claseAnterior", "claseNueva"); puede parecer tan versátil, hay que tomar en cuenta lo siguiente:

  • replace() reemplaza solo una clase (una anterior por una nueva). Reemplazar más clases requiere más lógica en código
  • En navegadores antiguos no es soportado (a junio de 2023, todos los navegadores de escritorio y móviles lo soportan. Excepto Internet Explorer)
  • Este método carece de alerta de error si una clase no es hallada.
  • Incrementa la carga del navegador si múltiples clases deben ser reemplazadas desde varias invocaciones de este método.

Método cloneNode()

Crea una copia de todos los atributos de un elemento o nodo del DOM y devuelve un nuevo elemento al DOM. También permite insertar ese elemento en alguna parte del DOM y usar las propiedades copiadas para crear nuevos elementos.

var clonar = referenciaElemento.cloneNode( --deep-- );

        // Copia todo
        var clonar = referenciaElemento.cloneNode( true );
        
        // Copia solo el elemento
        var clonar = referenciaElemento.cloneNode( false );

cloneNode() NO copia las reglas CSS porque estas no son parte natural de la estructura del DOM. Sin embargo, estilos CSS directamente insertados en las propiedades del nodo clonado sí se copian.

El parámetro (deep) representa uno de dos valores explícitos: true / false.

  • 'deep' con valor 'true': se copia (clona) elementos padre junto a hijos (descendientes); es decir, se copia todo el sub árbol de elementos.
  • 'deep' con valor 'false': se copia (clona) SOLO el elemento apuntado por cloneNode().
  • OJO: Elementos void (aquellos que no tienen ancestros), como <img> o <input> no son clonables.

El código ejecutado abajo muestra dos elementos (o nodos). Se duplicará solo el primer ancestro del contenedor. Pero luego del segundo clon su identificador IDs se repite. Ese error (los ID solo pueden estar una vez en un solo elemento) se eliminaría si solo clonamos nodos con clases.

---id-Elemento hijo

--- ---id-Elemento sobrino



Propiedad textContent

Obtiene o define el contenido textual y sub elementos como espacios y tags, tomados desde un elemento HTML. Incluso puede obtener y definir los elementos texto de los descendientes de un elemento padre.

Así mismo, textContent puede borrar el resto de elementos hijo de un elemento padre al agregar en este un solo contenido de texto. Por lo que los descendientes del padre se borrarán del DOM.

En el siguiente ejemplo, el texto del poema de J.J. de Olmedo no se lo está duplicando o copiando. Se lo está referenciando desde el elemento HTML original hacia otro elemento. Eso se comprueba si modificásemos el texto original: también se mostrará alterado en el recuadro celeste.

"y cuando vive y crece
en este suelo bajo, y cuanto se remonta
hasta el cielo estrellado (...)"




Propiedad innerText

Capta nodos texto de elementos HTML padres y descendientes. La captura es similar a seleccionar y copiar un texto con el cursos del mouse. Así mismo, puede remover todos los hijos nodo y reemplazarlo con un solo string de texto.

innerText es un método 'desinteresado', pues solo obtiene la línea de texto pura; y no toma en consideración subnodos como espacios en blanco, de tabs o de barra espaciadora. Muestra el texto puro, sin formato.

Este texto tiene reglas CSS Texto con 'display:none'

El texto capturado por textContent:

El texto capturado por innerText:



DIFERENCIAS

  • elemento.textContent
  • Obtiene el contenido de todos los elementos HTML susceptibles de texto (como <script>, o <style>)
  • Devuelve cada elemento de texto en un nodo
  • NO crea conversión del texto a HTML. Es decir, trata el el texto com plano, sin marcado de etiquetas.
  • Al no resultar el texto en uno interpretable por el navegador se previene ataquesXSS (Cross-site scripting) que puedan ser insertados mediante JS hacia el DOM.

  • elemento.innerText
  • Obtiene solo el contenido textual humanamente legible
  • No devuelve el contenido textual de elementos ocultos
  • NO crea conversión del texto a HTML. Es decir, trata el el texto com plano, sin marcado de etiquetas.
  • Los campos de ingreso de texto deben ser validadas para impedir que código dañino influya en el servidor o en otros datos.

  • elemento.innerHTML
  • Obtiene texto, etiquetas y lenguaje de marcado.
  • Sí transforma el texto convertido y marcado para ser interpretado por el navegador dentro del DOM. Por lo que el potencial peligro de que se cuele (en un campo de ingreso de texto) una porción de código dañino es alto.
  • Los campos de ingreso de texto deben ser validadas.

BIBLIOGRAFÍA:
3ra edición. 2018. Marijn Haverbeke. No starch press.
Mozilla Foundation.
David Flanagan (2020). Editorial O'Reilly.
Ved Antani, Stoyan Stefanov (2017). Packt Publishing.
Editorial Packt.
Information and Computation. Elsevier.
ACM SIGCSE BulletinVolume 19Issue 1Feb