DocumentFragment: qué es y cómo (no) combatirlo

Descargo de responsabilidad
Parece que estoy comenzando una nueva serie de artículos, un poco aburrida y puramente utilitaria. Explicarán los puntos que a menudo causan dificultades a mis alumnos. Si eres un desarrollador web experimentado, lo más probable es que no te interese. Si estás esperando perversiones en el poder del JS del viernes, no estarán aquí, por desgracia.


Una de las cosas que los estudiantes regularmente tienen problemas para entender es DocumentFragment. En general, no puedo culparlos. Con simplicidad externa, tiene varias propiedades no evidentes e incluso contraintuitivas. En este artículo quiero recopilar todo lo que un principiante necesita saber sobre él.

imagen

Que es esto


Un DocumentFragment es un contenedor que puede contener un número arbitrario de elementos DOM. Si simplemente, puedes imaginarlo como un cubo. Los elementos se apilan en él para que en el momento adecuado se puedan descargar de inmediato.

Cómo crear


Elemental

var fragment = document.createDocumentFragment(); 

También hay otras formas, pero sobre ellas a continuación.

Porque necesito


Como escribí anteriormente, para almacenar elementos DOM. "Pero pueden almacenarse en una diva ordinaria", puede objetar el lector. Es cierto, sin embargo, que el fragmento tiene una propiedad única que lo convierte en el mejor candidato para este papel. Considere el siguiente código:

 var fragment = document.createDocumentFragment(); var parentDiv = document.createElement("div"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); fragment.appendChild(div1); fragment.appendChild(div2); //   parentDiv.appendChild(fragment); console.log(parentDiv.children); 

¿Qué nos dirá la consola? Una persona que no parentDiv familiarizada con DocumentFragment podría pensar que parentDiv tendrá un fragment secundario. Pero, de hecho, tendrá dos hijos: div1 y div2 . El hecho es que el fragmento en sí no es un elemento DOM, es solo un contenedor para elementos DOM. Y cuando se pasa como argumento a métodos como appendChild o insertBefore , no se incrusta en el árbol DOM, sino que incrusta su contenido allí.

¿Pero por qué lo necesitas?


La propiedad "cubo" es, por supuesto, buena, pero ¿cómo es esto útil en la práctica? DocumentFragment tiene dos áreas principales de aplicación.

1. Almacenar piezas de HTML que no tienen un ancestro común.

Hay situaciones en las que necesitamos reemplazar el contenido de un elemento, pero no tocamos el elemento en sí. Supongamos que usamos delegación de eventos, y todos los controladores de eventos que ocurren en elementos internos se cuelgan en un div externo. En este caso, DocumentFragment es ideal para nosotros:

 div.innerHTML = ""; div.appendChild(fragmentWithAllContent); 

"¿Pero podemos agregar elementos al div de inmediato a medida que los creamos?" - preguntará el lector corrosivo. Podemos, pero no vale la pena, y por eso.

2. Mejora del rendimiento en el caso de múltiples insertos.

El hecho es que cada vez que cambiamos algo en el árbol DOM activo, el navegador tiene que hacer muchos cálculos. Puedes leer más sobre esto aquí , por ejemplo. En este artículo, nos limitamos a mencionar que hay una bestia tan terrible: el reflujo. Cuando agregamos un elemento a la página, esta bestia se despierta y consume un poco de tiempo de procesador. Si agregamos cien elementos, la bestia se despertará cien veces y morderá cien veces. Para el usuario, esto puede ser un "congelamiento" bastante notable.

Cuando agregamos un elemento al DocumentFragment, esto no causa reflujo, porque el fragmento no es (y básicamente no puede ser) parte del árbol DOM activo. Y lo más importante: cuando insertamos el contenido de un fragmento usando appendChild u otros métodos similares, independientemente de cuántos elementos hay dentro del fragmento, el reflujo se llama solo una vez .

Para mayor claridad, hice un punto de referencia simple para que el lector pueda ver personalmente la diferencia.

Upd: el camarada nuit dijo que para Chrome moderno mis palabras ya no son ciertas. En él, el reflujo no se ejecuta antes de lo necesario, y gracias a esto, el código sin DocumentFragment en realidad funciona más rápido , y con otros navegadores no es tan obvio. Por lo tanto, antes de decidir si usar fragmentos, necesita perfiles e investigación sobre el público objetivo del sitio.

Matices


Hay dos características que hacen que los principiantes a menudo sean difíciles de usar fragmentos. Primero: como escribí anteriormente, un fragmento no es un elemento DOM . Esto significa que carece de muchos métodos y propiedades familiares, en particular, innerHTML . Por lo tanto, no puede simplemente convertir una cadena en el contenido de un fragmento. Cómo hacerlo no es fácil, se describirá a continuación.

La segunda característica: el fragmento cuando se usa "botín". Más precisamente, se vacía. Cuando hacemos div.appendChild(fragment) , todos los hijos del fragmento están div.appendChild(fragment) en un div . Y dado que un elemento no puede tener más de un padre, esto significa que se eliminan del fragmento. Para evitar este comportamiento cuando no es deseable, puede usar cloneNode .

etiqueta <template>


Hay un lugar donde puede encontrar DocumentFragment sin crearlo a través de JS. Esta es la propiedad de content del elemento de plantilla.

La <template> inventó específicamente para almacenar fragmentos de código HTML, pero no para cargar el navegador antes de tiempo. Lo que está dentro de esta etiqueta no se convierte en parte del árbol DOM activo. En particular (los recién llegados a menudo también se encuentran con esto), no se pueden encontrar usando querySelector . Los elementos creados a partir del código HTML dentro de la etiqueta <template> no se convierten en elementos secundarios. En cambio, JavaScript puede acceder a ellos a través de la propiedad de content , lo cual es una sorpresa. - solo DocumentFragment.

Usando el elemento de plantilla, puede crear un fragmento de una cadena:

 function createFragmentFromString(str){ var template = document.createElement("template"); template.innerHTML = str; return template.content; } 

Epílogo


Si eres nuevo en el desarrollo web, espero que hayas aprendido mucho. Si eres un desarrollador experimentado, es posible que desees complementar este artículo con algo, en cuyo caso no dudes en escribirlo en los comentarios. Gracias por leer y que tengas un buen día.

Source: https://habr.com/ru/post/es413287/


All Articles