Hola y bienvenidos de nuevo a la serie "JavaScript y DOM". La última vez cubrimos algunos conceptos básicos de JavaScript y abordamos varios aspectos del Modelo de Objeto de Documento, incluyendo cómo acceder a los nodos y atravesar el DOM. Hoy veremos cómo manipular los elementos dentro del DOM y discutiremos el modelo de evento del navegador..
En la última lección, cubrimos los pasos involucrados en el acceso a una colección de nodos DOM o un nodo DOM singular. La magia real ocurre cuando luego manipulas ciertas propiedades que resultan en lo que se conoce ampliamente como "comportamiento".
Cada nodo DOM único tiene una colección de propiedades; la mayoría de estas propiedades proporcionan abstracciones a ciertas funcionalidades. Por ejemplo, si tiene un elemento de párrafo con un ID de 'introducción', podría cambiar fácilmente el color de ese elemento a través de la API DOM:
document.getElementById ('intro'). style.color = '# FF0000';
Para ilustrar la naturaleza objeto / propiedad de esta API, podría ser más fácil de entender si la dividimos asignando cada objeto a una variable:
var myDocument = document; var myIntro = myDocument.getElementById ('intro'); var myIntroStyles = myIntro.style; // Y ahora, podemos establecer el color: myIntroStyles.color = '# FF0000';
Ahora que tenemos una referencia al objeto 'estilo' del párrafo, podemos agregar otros estilos CSS:
myIntroStyles.padding = '2px 3px 0 3px'; myIntroStyles.backgroundColor = '#FFF'; myIntroStyles.marginTop = '20px';
Aquí solo estamos usando los nombres de propiedades básicas de CSS. La única diferencia es que donde normalmente encontraría un guión ('-'), el texto está en camello. Así que en lugar de 'margin-top' usamos 'marginTop'. Lo siguiente, por ejemplo, no lo haría trabajar y produciría un error de sintaxis:
myIntroStyles.padding-top = '10em'; // Produce un error de sintaxis: // - El carácter '-' es el operador menos en JavaScript. // - Además, no hay tal nombre de propiedad.
Se puede acceder a las propiedades de forma similar a una matriz. Entonces, con este conocimiento podríamos crear una pequeña función para cambiar cualquier estilo de un elemento dado:
function changeStyle (elem, property, val) elem.style [propiedad] = val; // Observe los corchetes utilizados para acceder a la propiedad // Usaría el complemento anterior así: var myIntro = document.getElementById ('intro'); // Agarra el párrafo introductorio changeStyle (myIntro, 'color', 'red');
Esto es solo un ejemplo: para ser honesto, probablemente no sea una función muy útil ya que, sintácticamente, es más rápido usar los medios convencionales que se muestran anteriormente (por ejemplo,. elem.style.color = 'rojo').
Además de la propiedad 'estilo', hay muchos otros que puede usar para manipular ciertos aspectos de un nodo / elemento. De hecho, si tiene Firebug instalado, debe intentar "inspeccionar un elemento", luego haga clic en la pestaña "DOM" (normalmente a la derecha o debajo del panel de visualización del elemento) para ver todas sus propiedades:
Se puede acceder a todas las propiedades utilizando la notación de puntos convencional (por ejemplo, Element.tabIndex). No todas las propiedades son tipos de datos primitivos (cadenas, números, booleanos, etc.); La propiedad 'estilo', por ejemplo, que analizamos anteriormente, es un objeto que contiene sus propias propiedades. Muchas de las propiedades de un elemento serán legibles solamente; Lo que quiero decir con esto es que no puedes cambiar su valor. Por ejemplo, no puede cambiar directamente la propiedad 'parentNode' de un nodo. El navegador generalmente generará un error si intenta cambiar una de estas propiedades de solo lectura: por ejemplo, ERROR: "establecer una propiedad que solo tiene un captador". Es solo algo de lo que hay que ser consciente ...
Un requisito común es cambiar el contenido dentro de un elemento. Hay algunas maneras diferentes de hacer esto. De lejos, la forma más fácil es usar la propiedad 'innerHTML', como esta:
var myIntro = document.getElementById ('intro'); // Reemplazando el contenido actual con contenido nuevo: myIntro.innerHTML = 'Nuevo contenido para el increíble ¡párrafo!'; // Agregar al contenido actual: myIntro.innerHTML + = '… algo más de contenido ...';
El único problema con este método es que no está especificado en ningún estándar y no está en la especificación DOM. Si no te preocupa eso, entonces adelante y úsalo. De todos modos, normalmente es mucho más rápido que los métodos DOM convencionales, que veremos a continuación..
Al crear contenido a través de la API DOM, debe tener en cuenta dos tipos diferentes de nodos, un nodo de elemento y un nodo de texto. Hay muchos otros tipos de nodos, pero estos dos son los únicos importantes por ahora..
Para crear un elemento, utilice el método 'createElement' y para crear un nodo de texto, use el método 'createTextNode', ambos se muestran a continuación:
var myIntro = document.getElementById ('intro'); // Queremos agregar algo de contenido al párrafo: var someText = 'Este es el texto que quiero agregar'; var textNode = document.createTextNode (someText); myIntro.appendChild (textNode);
Aquí estamos usando el método 'appendChild' para agregar nuestro nuevo nodo de texto al párrafo. Hacerlo de esta manera lleva un poco más de tiempo que el método no estándar de innerHTML, pero aún así es importante conocer ambas formas para que pueda tomar la decisión correcta. Aquí hay un ejemplo más avanzado utilizando métodos DOM:
var myIntro = document.getElementById ('intro'); // Queremos agregar un nuevo anclaje al párrafo: // Primero, creamos el nuevo elemento de anclaje: var myNewLink = document.createElement ('a'); // myNewLink.href = 'http://google.com'; // myNewLink.appendChild (document.createTextNode ('Visit Google')); // Visite Google // Ahora podemos agregarlo al párrafo: myIntro.appendChild (myNewLink);
También hay un método DOM 'insertBefore' que se explica por sí mismo. Usando estos dos métodos ('insertBefore' & 'appendChild') podemos crear nuestra propia función 'insertAfter':
// 'Target' es el elemento que ya está en el DOM // 'Bullet' es el elemento que desea insertar la función insertAfter (target, bullet) target.nextSibling? target.parentNode.insertBefore (bullet, target.nextSibling): target.parentNode.appendChild (bullet); // Estamos usando un operador ternario en la función anterior: // Su formato: ¿CONDICIÓN? EXPRESIÓN SI ES VERDADERO: EXPRESIÓN SI ES FALSO;
La función anterior verifica la existencia del próximo hermano del objetivo dentro del DOM, si existe, insertará el nodo 'bullet' antes del próximo hermano del objetivo; de lo contrario, asumirá que el objetivo es el último elemento secundario de un elemento y así Está bien agregar la bala como un hijo del padre. La API DOM no nos da ningún método 'insertAfter' porque no es necesario, podemos crearlo nosotros mismos.
Hay mucho más que aprender acerca de cómo manipular elementos dentro del DOM, pero lo anterior debe ser una base suficiente sobre la que se pueda construir..
Los eventos del navegador están en el núcleo de cualquier aplicación web y la mayoría de las mejoras de JavaScript. Es a través de estos eventos que definimos cuando algo va a suceder. Si tiene un botón en su documento y necesita que se realice una validación de formulario al hacer clic, entonces usará el evento 'clic'. A continuación se muestra una descripción general de la mayoría de los eventos de navegador estándar:
Nota: Como comentamos la última vez, el DOM y el lenguaje de JavaScript son dos entidades separadas. Los eventos del navegador son parte de la API DOM, no son parte de JavaScript.
Hay muchos más eventos para elegir. Los que se muestran arriba son los principales que encontrará con frecuencia en el código JavaScript. Tenga en cuenta que algunos de ellos tienen diferencias sutiles entre navegadores. Además, tenga en cuenta que muchos navegadores implementan eventos propietarios, por ejemplo, hay bastantes eventos específicos de Gecko, como 'DOMContentLoaded' o 'DOMMouseScroll'; puede leer más sobre estos aquí: https://developer.mozilla.org / es / Gecko-Specific_DOM_Events
Hemos cubierto los eventos reales, pero todavía tenemos que discutir el proceso de adjuntar una función a un evento. Aquí es donde ocurre la magia. Los eventos enumerados anteriormente ocurrirán independientemente de si ha escrito o no JavaScript, así que para aprovechar su poder tiene que registrar "manejadores de eventos", este es un término elegante para describir una función que se usa para manejar un evento. Aquí hay un ejemplo simple usando el BASIC Modelo de registro de evento (también conocido como "registro de evento tradicional"):
Registro de evento básico:
// JavaScript: var myElement = document.getElementById ('my-button'); // Esta función será nuestro controlador de eventos: function buttonClick () alert ('¡Has hecho clic en el botón!'); // Esta es la parte de registro de eventos: myElement.onclick = buttonClick;
Tenemos un botón HTML con un ID de 'mi-botón' y lo hemos accedido utilizando el comando 'document.getElementById'. Luego estamos creando una nueva función que luego se asigna a la propiedad DOM 'onclick' del botón. Eso es todo al respecto!
El modelo de "registro de eventos básico" es tan simple como es posible. Prefijo el evento que está buscando con 'on' y acceda a él como una propiedad de cualquier elemento con el que esté trabajando. Esta es esencialmente la versión discreta de hacer algo como esto (que no recomiendo):
El manejo de eventos en línea (usando atributos HTML) es muy molesto y hace que su sitio web sea mucho más difícil de mantener. Es mejor usar JavaScript discreto y tenerlo todo contenido dentro de los archivos '.js' respectivos que se pueden incluir en el documento cuando sea necesario. Si bien estamos en el tema de JavaScript discreto, me gustaría corregir la idea errónea de que las bibliotecas como jQuery hacen que sea "posible codificar de forma discreta", esto no es cierto. Cuando estás usando jQuery es igual de fácil hacer las cosas de la manera incorrecta. La razón por la que no debe usar el manejo de eventos en línea es exactamente la misma razón por la que no debe aplicar los estilos CSS en línea (uso).
Registro avanzado de eventos:
No dejes que este nombre te engañe, solo porque se llame "avanzado" no significa que sea mejor usarlo; de hecho, la técnica que discutimos anteriormente ("registro de eventos básicos") es perfectamente adecuada la mayor parte del tiempo. Sin embargo, el uso de la técnica básica tiene una limitación clave; no puede enlazar más de una función a un evento. En realidad, esto no es tan malo, ya que puede llamar a cualquier número de otras funciones desde esa única función, pero si necesita más control, entonces hay otra forma de registrar a los manejadores, ingrese el "modelo avanzado de registro de eventos".
Este modelo le permite vincular varios controladores a un solo evento, lo que significa que se ejecutarán múltiples funciones cuando se produzca un evento. Además, este modelo le permite eliminar fácilmente cualquiera de los controladores de eventos enlazados.
Estrictamente hablando, hay dos modelos diferentes en esta categoría; Los W3C y los de Microsoft. El modelo W3C es compatible con todos los navegadores modernos, además de IE, y el modelo de Microsoft solo es compatible con IE. Así es como utilizarías el modelo de W3C:
// FORMAT: target.addEventListener (type, function, useCapture); // Ejemplo: var myIntro = document.getElementById ('intro'); myIntro.addEventListener ('clic', introClick, falso);
Y aquí es lo mismo, pero para IE (modelo de Microsoft):
// FORMAT: target.attachEvent ('on' + type, function); // Ejemplo: var myIntro = document.getElementById ('intro'); myIntro.attachEvent ('onclick', introClick);
Y aquí está la función 'introClick':
function introClick () alert ('¡Has hecho clic en el párrafo!');
Debido al hecho de que ninguno de los modelos funciona en todos los navegadores, es una buena idea combinarlos en una función personalizada. Aquí hay una función 'addEvent' muy básica, que funciona en todos los navegadores:
función addEvent (elem, type, fn) if (elem.attachEvent) elem.attachEvent ('on' + type, fn); regreso; if (elem.addEventListener) elem.addEventListener (type, fn, false);
La función comprueba las propiedades 'attachEvent' y 'addEventListener' y luego usa uno de los modelos que dependen de esa prueba. Ambos modelos también permiten eliminar los controladores de eventos, como se muestra en esta función 'removeEvent':
function removeEvent (elem, type, fn) if (elem.detachEvent) elem.detachEvent ('on' + type, fn); regreso; if (elem.removeEventListener) elem.removeEventListener (type, fn, false);
Usarías las funciones como esta:
var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', function () alert ('ME HAZ CLICKED ME !!!'););
Observe que pasamos una función sin nombre como el tercer parámetro. JavaScript nos permite definir y ejecutar funciones sin nombrarlas; Las funciones de este tipo se llaman "funciones anónimas" y pueden ser muy útiles, especialmente cuando necesita pasar una función como parámetro a otra función. Podríamos haber puesto nuestra función 'introClick' (definida anteriormente) como tercer parámetro, pero a veces es más conveniente hacerlo con una función anónima..
Si desea que se produzca una acción en un evento solo la primera vez que se hace clic, puede hacer algo como esto:
// Tenga en cuenta que ya hemos definido las funciones addEvent / removeEvent // (para poder usarlas deben incluirse) var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', oneClickOnly); function oneClickOnly () alert ('WOW!'); removeEvent (myIntro, 'click', oneClickOnly);
Estamos eliminando el controlador tan pronto como el evento se dispara por primera vez. No hemos podido utilizar una función anónima en el ejemplo anterior porque necesitábamos conservar una referencia a la función ('oneClickOnly') para poder eliminarla más tarde. Dicho esto, en realidad es posible lograrlo con una función sin nombre (anónima):
addEvent (myIntro, 'click', function () alert ('WOW!'); removeEvent (myIntro, 'click', argument.callee););
Estamos siendo bastante descarados aquí al hacer referencia a la propiedad 'llamada' del objeto 'argumentos'. El objeto 'argumentos' contiene todos los parámetros pasados de CUALQUIER función y también contiene una referencia a la función en sí misma ('persona llamada'). Al hacer esto, eliminamos completamente la necesidad de definir una función nombrada (por ejemplo, la función 'oneClickOnly' que se mostró anteriormente).
Aparte de las obvias diferencias sintácticas entre los W3C y la implementación de Microsoft, hay otras discrepancias que vale la pena mencionar. Cuando vincula una función a un evento, la función debe ejecutarse dentro del contexto del elemento, por lo que la palabra clave 'this' dentro de la función debe hacer referencia al elemento; utilizando el modelo de registro de eventos básico o el modelo avanzado de W3C, esto funciona sin fallas, pero la implementación de Microsoft falla. Aquí hay un ejemplo de lo que debería Ser capaz de hacer dentro de las funciones de manejo de eventos:
function myEventHandler () this.style.display = 'none'; // Funciona correctamente, 'this' hace referencia al elemento: myIntro.onclick = myEventHandler; // Funciona correctamente, 'this' hace referencia al elemento: myIntro.addEventListener ('click', myEventHandler, false); // NO funciona correctamente, 'this' hace referencia al objeto Window: myIntro.attachEvent ('onclick', myEventHandler);
Hay algunas maneras diferentes de evitar / solucionar este problema. Con mucho, la opción más fácil es usar el modelo básico, ya que casi no hay incoherencias entre los navegadores cuando se usa este modelo. Sin embargo, si desea usar el modelo avanzado y necesita la palabra clave 'this' para hacer referencia al elemento correctamente, debería echar un vistazo a algunas de las funciones 'addEvent' más ampliamente adoptadas, específicamente las de John Resig o Dean Edward (no se ve). ni siquiera utilice el modelo avanzado, excelente!).
Un aspecto importante del manejo de eventos que aún no hemos discutido es algo llamado "objeto de evento". Siempre que vincule una función a un evento, es decir, cada vez que cree un controlador de eventos, la función pasará a un objeto. Esto sucede de forma nativa, por lo que no es necesario realizar ninguna acción para inducirlo. Este objeto de evento contiene una variedad de información sobre el evento que acaba de ocurrir; También contiene métodos ejecutables que tienen diversos efectos de comportamiento en el evento. Pero, como era de esperar, Microsoft eligió su propia manera de implementar esta "característica"; Los navegadores IE no pasan este objeto de evento, en su lugar, debe acceder a él como una propiedad del objeto de ventana global; esto no es realmente un problema, es solo una molestia:
function myEventHandler (e) // Observe el argumento 'e' ... // Cuando se llama a esta función, como resultado de la activación // del evento, se pasará el objeto del evento (en agentes compatibles con W3C) // Hagamos ' e 'compatible con varios navegadores: e = e || window.event; // Ahora podemos hacer referencia segura a 'e' en todos los navegadores modernos. // Nosotros uniríamos nuestra función a un evento aquí abajo ...
Para verificar la existencia del objeto 'e' (el "objeto de evento") utilizamos un operador OR (lógico) que básicamente dicta lo siguiente: si 'e' es un valor "falsy" (nulo, indefinido, 0) etc.) luego asigne 'window.event' a 'e'; de lo contrario solo usa 'e'. Esta es una forma rápida y fácil de obtener el objeto de Evento real en un entorno de navegador cruzado. Si no se siente cómodo con el uso de operadores lógicos fuera de una declaración IF, esta construcción podría ser más adecuada para usted:
si (! e) e = window.event; // No se necesita una declaración ELSE ya que 'e' // ya estará definido en otros navegadores
Lamentablemente, algunos de los comandos y propiedades más útiles de este objeto de evento se implementan de manera inconsistente en todos los navegadores (es decir, IE y todos los demás). Por ejemplo, la cancelación de la acción predeterminada de un evento se puede lograr usando el método 'preventDefault ()' del objeto Event, pero en IE solo se puede lograr usando la propiedad 'returnValue' del objeto. Entonces, nuevamente, tenemos que usar ambos para acomodar a todos los navegadores:
función myEventHandler (e) e = e || window.event; // Evitar la acción predeterminada de un evento: if (e.preventDefault) e.preventDefault (); else e.returnValue = false;
La acción predeterminada de un evento es lo que normalmente sucede como resultado de la activación de ese evento. Al hacer clic en un enlace de anclaje, la acción predeterminada es que el navegador navegue a la ubicación especificada en el atributo 'href' de ese enlace. Pero a veces querrás deshabilitar esta acción por defecto..
La molestia 'returnValue' / 'preventDefault' no es por sí sola; muchas otras propiedades del objeto Evento se implementan de manera inconsistente, por lo que esto si / else / or check model es una tarea requerida.
Muchas de las bibliotecas de JavaScript de hoy normalizan el objeto del evento, lo que significa que comandos como 'e.preventDefault' estarán disponibles en IE, aunque, debe tener en cuenta que detrás de la escena la propiedad 'returnValue' todavía se está utilizando.
La propagación de eventos, también conocida como "propagación de eventos", es cuando un evento se activa y luego ese evento "burbujea" a través del DOM. Lo primero que se debe tener en cuenta es que no todos los eventos se propagan, pero para aquellos que lo hacen, así es como funciona:
El evento se dispara en el elemento objetivo. Luego, el evento se dispara a todos y cada uno de los antepasados de ese elemento, el evento se infla a través del DOM hasta que alcanza el elemento más alto:
Como se muestra en el gráfico anterior, si se hace clic en un ancla dentro de un párrafo, el evento de clic del ancla se activará primero y luego, a continuación, se activará el evento de clic de los párrafos, hasta que se alcance el elemento del cuerpo (el cuerpo es el elemento DOM más alto). que tiene un evento de clic).
Estos eventos se activarán en ese orden, no todos ocurren al mismo tiempo..
La idea de un evento de burbujeo puede no tener mucho sentido al principio, pero al final queda claro que es una parte fundamental de lo que consideramos "comportamiento normal". Cuando vincula un controlador al evento de clic del párrafo, espera que se active cada vez que se haga clic en el párrafo, ¿verdad? Bueno, eso es exactamente lo que garantiza el "evento burbujeante": si el párrafo tiene varios hijos, (s,
Este comportamiento de burbujeo puede detenerse en CUALQUIER momento durante el proceso. Por lo tanto, si solo desea que el evento se incremente en el párrafo pero no más (no en el nodo del cuerpo), puede usar otro método útil que se encuentra en el objeto Evento, "stopPropagation":
function myParagraphEventHandler (e) e = e || window.event; // Evita que el evento se forme: if (e.stopPropagation) // navegadores compatibles con W3C: e.stopPropagation (); else // IE: e.cancelBubble = true; // La función se vincularía al evento click del párrafo: // Usando nuestra función addEvent personalizada: addEvent (document.getElementsByTagName ('p') [0], 'click', myParagraphEventHandler);
Digamos, por ejemplo, que tiene una tabla masiva con muchas filas de datos. Vincular un controlador de eventos de clic a cada uno
var myTable = document.getElementById ('my-table'); myTable.onclick = function () // Manejo de incompatibilidades con el navegador: e = e || window.event; var targetNode = e.target || e.srcElement; // Probar si se hizo clic en una TR: if (targetNode.nodeName.toLowerCase () === 'tr') alert ('¡Has hecho clic en una fila de la tabla!');
La delegación de eventos se basa en eventos burbujeantes. El código anterior no funcionaría si se detuviera el burbujeo antes de llegar al nodo 'tabla'.
Hemos cubierto cómo manipular los elementos DOM y hemos discutido, con bastante profundidad, el modelo de evento del navegador. Espero que hayas aprendido algo hoy! Como de costumbre, si tiene alguna pregunta, no dude en preguntar.