Vanya va a la abuela o JavaScript dinámico adaptativo

Mientras trabajamos en la adaptación, de vez en cuando tenemos que cambiar la apariencia de los objetos. Por lo general, es suficiente para nosotros registrar varias consultas de medios y podemos hacerlo. En algunos casos, necesitamos cambiar el orden de los elementos, y aquí, por ejemplo, la propiedad del pedido de la tecnología Flexbox o Grid viene al rescate. Pero hay momentos en que esto no nos ayuda.

La idea de la adaptación dinámica está diseñada para simplificar la vida de un diseñador de diseño y adaptar rápidamente elementos de diseño complejos.


Por ejemplo, mira este caso. Aquí tenemos la siguiente estructura: encabezado y algo de texto.



Pero con cierto permiso, el diseñador indicó que el texto que estaba debajo del encabezado ahora debería estar en un lugar completamente diferente en la estructura.



Por lo general, los diseñadores de diseño resolvieron este problema de una de dos maneras. Te recordaré rápidamente a ellos.

Lo mostraré como un ejemplo de una situación de vida en la que el chico de la ciudad, Vanya, realmente quería venir con su abuela para el verano. Pero no porque echara de menos a su abuela, sino porque quería ir al río con la niña Olya. Y quería hacer esto con una resolución inferior a 992px.



¿Cómo resolveremos el problema de Vani?
Podemos hacer lo siguiente: Ocultar Vanya en la ciudad con este permiso, y mostrar Vanya en el río con Olya. Pero para esto necesitamos hacer un clon de Vani.
Aquí está la ciudad (original) Vanya. Pero Vanya, de hecho, ya está en el río con Olya.



Pero inicialmente este río Vanya está oculto en CSS. Escribimos una consulta de medios con los parámetros que necesitamos, en la que mostramos la nueva Vanya y ocultamos la antigua Vanya (ciudad).



Todo salió bien para nosotros: con el permiso que necesitamos, Vanya se encuentra bajo Olya.



En consecuencia, cuando la resolución es superior a 992 px, Vanya regresa inmediatamente a la ciudad.



Y todo parece estar bien, Vanya con Olya cuando es necesario. Puedes dispersar ...

Pero no tan simple. De hecho, algunos clientes se oponen mucho al contenido duplicado. Esto se debe a algunos problemas con el SEO y, en principio, a la creación de bloques innecesarios.

Ahora tenemos suerte, en nuestra situación Vanya es un poco pequeña, y resulta que Vanya es un tipo muy grande, masivo y bombeado, que respectivamente requiere muchas líneas de código. Y en esta situación, incluidos los clientes, le piden que mueva el objeto DOM usando JavaScript.

Esto se puede hacer aproximadamente de la siguiente manera. Para empezar, tengo que eliminar Vanya, que es un clon, en HTML y dejar Vanya original. En CSS, comente todas las líneas que originalmente teníamos para otra opción de ajuste. Y agregue aquí un código JS tan simple.

// 
const parent_original = document.querySelector('.content__blocks_city');
const parent = document.querySelector('.content__column_river');
const item = document.querySelector('.content__block_item');

//   
window.addEventListener('resize', move);

//
function move(){
	const viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
	if (viewport_width <= 992) {
		if (!item.classList.contains('done')) {
			parent.insertBefore(item, parent.children[2]);
			item.classList.add('done');
		}
	} else {
		if (item.classList.contains('done')) {
			parent_original.insertBefore(item, parent_original.children[2]);
			item.classList.remove('done');
		}
	}
}

// 
move();

Primero, declaramos las variables donde "marcamos" la ciudad, el río y el Vani original. A continuación, comenzamos a "escuchar" el ancho de la pantalla para captar el momento en que necesitamos mover a Vanya. Después de obtener el ancho y establecer la condición en la que verificamos que el ancho sea menor o igual a 992px. Al mover a Vanya, le asignamos una clase, por lo que verificamos la presencia de una clase para que este movimiento no ocurra todo el tiempo (Vanya se movió una vez y eso es todo, no necesita transferirlo constantemente). Y realizamos la transferencia dentro del padre (al río), aquí indicamos la posición en el árbol DOM donde necesitamos mover Vanya (debajo de Olya, sobre Olya). Y cuando tengamos una resolución superior a 992 px, debemos devolver a Vanya al lugar.

Tratamos de ver que nuestra Vanya nuevamente se movió bajo Olya.

Y todo está bien: no tenemos contenido duplicado, todo funciona bien, el cliente está satisfecho, todos están contentos. Pero este método tiene una serie de problemas asociados con la escritura del propio código js.
Entendemos que para un solo caso esto es normal: escribimos bien. Pero tuve la idea de automatizar este proceso. Supongamos que necesito lanzar 5-6 elementos completamente diferentes en la página. Para esta tarea, escribir un código js usando este método es bastante laborioso y requiere mucho tiempo. Y el segundo punto, que, naturalmente, ha presionado a muchos de ustedes, es la carga en la PC del usuario, porque aquí constantemente "escuchamos" el ancho de la pantalla. También quería optimizar este momento. También se puede optimizar en este código, pero desde que escribí una herramienta de automatización que llamé "dinámica adaptativa" , la optimicé allí.

Ahora te mostraré cómo funciona todo.

Después de conectar mi adaptativo dinámico, solo necesito trabajar con el archivo HTML y no es necesario acceder a JavaScript. Para hacer esto, necesito escribir el atributo data-da (da es la abreviatura de adaptativo dinámico) y especificar tres parámetros entre comillas, separados por comas.
El primer parámetro es dónde. Tomo una clase única del objeto donde quiero mover Vanya.
El segundo parámetro es cuál (en una fila). Mi objeto tiene dos elementos: cero y el primero (sabemos que en JavaScript todo comienza desde cero). Necesito insertar Vanya después del objeto "1", así que estoy escribiendo el parámetro "2".

El tercer parámetro es cuándo. En el tercer parámetro, escribo "992", esta es la resolución debajo de la cual Vanya realmente quiere llegar a Olya.

El segundo y tercer parámetro son opcionales. Por defecto, el objeto se moverá al final del bloque con una resolución inferior a 768 px.



Verificamos. Todo funciona igual que en versiones anteriores, pero sin duplicación de contenido y todo está automatizado.

Considera las comodidades.

Ahora, para mover Vanya sobre Olya, solo necesito cambiar el número de serie en el segundo parámetro de atributo. Y Vanya ya habrá terminado con Olya.



Conveniente, ¿verdad? Puedo hacer lo mismo con el tercer parámetro de punto de interrupción. Puedo especificar, por ejemplo, 1280px. (data-da = "content__column_river, 1,1280"). Entonces Vanya irá a Olya un poco antes, precisamente cuando se alcanza este valor. Y lo mejor es que Vanya regresará a su lugar de forma absolutamente automática.

Para mayor comodidad, en lugar del número de serie en el segundo parámetro del atributo, puede especificar uno de los dos valores: primero o último. Cuando escribo primero, Vanya se moverá sobre el título "Soy un río". Se ubicará el primero dentro del objeto donde lo enviamos. En consecuencia, si especifica el último, nuestra Vanya estará en la parte inferior del objeto.



Veamos un par de ejemplos más. Supongamos que podemos enviar a Kolya al jardín de la lota y enviar a Anya al río a Van y Ola también, será más divertido. Escribimos para Kolya un atributo con parámetros
data-da = "content__column_garden, 2,992" . Y para Ani escribimos data-da = "content__column_river, 1.1280"

Buscamos: Anya, Olya y Vanya en el río. Kolya va a Lota un poco más tarde. A su regreso, todos los chicos están en sus lugares. Todo esta bien.

Juegue en CodePen: codepen.io/FreelancerLifeStyle/project/full/ZmWOPm

Por supuesto, puede tener una pregunta: ¿qué pasa con el evento que podría colgarse en objetos reubicables? Por ejemplo, inicialmente, al hacer clic en Anya, aparece una ventana con la inscripción: "Todo está bien". Veamos qué sucede cuando ella se muda con los chicos al río.
Muévase, haga clic: todo funciona bien, también se muestra la ventana. El evento permanece con el objeto cuando se mueve.



Y la segunda pregunta emocionante es la carga, que intenté minimizar. Ahora mostraré esto contando un poco sobre el código que está oculto bajo el capó de la adaptación dinámica.

En primer lugar, declaro algunas variables, incluida la creación de matrices, que completo a continuación. Obtengo el atributo, lo separo, lo convierto en una matriz para obtener elementos individuales. Luego, completo la posición original de los objetos para que luego puedan ser devueltos a la casa de sus padres (en la ciudad). Después de eso, completé la matriz de los elementos en sí, para poder ordenarla para que funcione correctamente.

let originalPositions = [];
let daElements = document.querySelectorAll('[data-da]');
let daElementsArray = [];
let daMatchMedia = [];
// 
if (daElements.length > 0) {
	let number = 0;
	for (let index = 0; index < daElements.length; index++) {
		const daElement = daElements[index];
		const daMove = daElement.getAttribute('data-da');
		if (daMove != '') {
			const daArray = daMove.split(',');
			const daPlace = daArray[1] ? daArray[1].trim() : 'last';
			const daBreakpoint = daArray[2] ? daArray[2].trim() : '767';
			const daDestination = document.querySelector('.' + daArray[0].trim())
			if (daArray.length > 0 && daDestination) {
				daElement.setAttribute('data-da-index', number);
				//   
				originalPositions[number] = {
					"parent": daElement.parentNode,
					"index": indexInParent(daElement)
				};
				//   
				daElementsArray[number] = {
					"element": daElement,
					"destination": document.querySelector('.' + daArray[0].trim()),
					"place": daPlace,
					"breakpoint": daBreakpoint
				}
				number++;
			}
		}
	}
	dynamicAdaptSort(daElementsArray);

	//    
	for (let index = 0; index < daElementsArray.length; index++) {
		const el = daElementsArray[index];
		const daBreakpoint = el.breakpoint;
		const daType = "max"; // MobileFirst   min

		daMatchMedia.push(window.matchMedia("(" + daType + "-width: " + daBreakpoint + "px)"));
		daMatchMedia[index].addListener(dynamicAdapt);
	}
}

Y al final, se implementa la optimización de la que hablé: en lugar de cambiar el tamaño de la ventana (escuchar cada cambio de ventana), solo escucharemos los cambios en el punto de interrupción usando window matchMedia. De esta forma, obtenemos la respuesta del navegador solo en algunos puntos: aquellos que indicamos en los atributos de nuestros objetos.

A continuación, recopilo una serie de estos puntos de interrupción y cuelgo un evento para cada punto de interrupción. En la función main dynamic_adapt, amplío todo esto dentro de la matriz que recopilamos y clasificamos, verifico la presencia de un punto de interrupción, muevo los elementos o los devuelvo a su lugar.

// 
function dynamicAdapt(e) {
	for (let index = 0; index < daElementsArray.length; index++) {
		const el = daElementsArray[index];
		const daElement = el.element;
		const daDestination = el.destination;
		const daPlace = el.place;
		const daBreakpoint = el.breakpoint;
		const daClassname = "_dynamic_adapt_" + daBreakpoint;

		if (daMatchMedia[index].matches) {
			// 
			if (!daElement.classList.contains(daClassname)) {
				let actualIndex = indexOfElements(daDestination)[daPlace];
				if (daPlace === 'first') {
					actualIndex = indexOfElements(daDestination)[0];
				} else if (daPlace === 'last') {
					actualIndex = indexOfElements(daDestination)[indexOfElements(daDestination).length];
				}
				daDestination.insertBefore(daElement, daDestination.children[actualIndex]);
				daElement.classList.add(daClassname);
			}
		} else {
			//  
			if (daElement.classList.contains(daClassname)) {
				dynamicAdaptBack(daElement);
				daElement.classList.remove(daClassname);
			}
		}
	}
	customAdapt();
}

Volver a la función de posición de inicio:

//   
function dynamicAdaptBack(el) {
	const daIndex = el.getAttribute('data-da-index');
	const originalPlace = originalPositions[daIndex];
	const parentPlace = originalPlace['parent'];
	const indexPlace = originalPlace['index'];
	const actualIndex = indexOfElements(parentPlace, true)[indexPlace];
	parentPlace.insertBefore(el, parentPlace.children[actualIndex]);
}

Naturalmente, muchos tienen una pregunta sobre Mobile First. No hay problemas: necesitamos cambiar solo algunas variables en el código (ya está funcionando eliminar esta configuración en HTML):

  1. Cuando recopilo una serie de puntos de interrupción, necesitamos cambiar "max" a "min" aquí. Y el evento será "escuchado" no por ancho máximo, sino por ancho mínimo.
  2. También debe cambiar la clasificación para que los objetos en la matriz estén alineados en un orden diferente cuando los clasifiquemos por punto de interrupción.

	// 
	function dynamicAdaptSort(arr) {
		arr.sort(function (a, b) {
			if (a.breakpoint > b.breakpoint) { return -1 } else { return 1 } // MobileFirst 
		});
		arr.sort(function (a, b) {
			if (a.place > b.place) { return 1 } else { return -1 }
		});
	}

También en dinámica adaptativa, uso un cierto método para obtener el índice cuando se arroja el objeto. Y aquí están los primeros defectos: tengo que excluir el objeto transferido. Aquellos. cuando transfiero objetos al mismo lugar, para que todo sea adecuado y los objetos se alineen en el orden que queremos, cuando transfiero, necesito excluir el objeto transferido previamente al mismo punto.

//       
function indexOfElements(parent, back) {
	const children = parent.children;
	const childrenArray = [];
	for (let i = 0; i < children.length; i++) {
		const childrenElement = children[i];
		if (back) {
			childrenArray.push(i);
		} else {
			//  
			if (childrenElement.getAttribute('data-da') == null) {
				childrenArray.push(i);
			}
		}
	}
	return childrenArray;
}

Pero conlleva otro problema de clasificación. Por ejemplo, si transfiero a los chicos al mismo tiempo a la misma posición, no podré controlar su orden.

Una de las formas de desarrollar esta adaptación dinámica puede ser, por ejemplo, la creación de varios puntos de interrupción. Ejemplo, necesitamos mover Vanya a Ola a 1280px, y a 992px Vanya para movernos al jardín, o regresar al lugar. Esta funcionalidad no está disponible ahora, por lo que aún queda trabajo por hacer. En general, para cubrir el 95% de las tareas cotidianas en la adaptación, la funcionalidad que es ahora es suficiente para mí. Para resolver el problema de mover elementos a otro lugar, necesito pasar unos segundos escribiendo un atributo. Y obtendré el resultado que necesito.

Naturalmente, exhorto absolutamente a todos los que les gustó la dinámica adaptativa a participar en sus pruebas, desarrollo y mejora tanto del funcionamiento como de la optimización del código. Y todo para que nuestros Kohl, Ani, Vani y Olya estén juntos cuando lo deseen.

Repositorio de GitHub - github.com/FreelancerLifeStyle/dynamic_adapt

Basado en el video " Adaptación dinámica // Adaptación rápida de objetos complejos en JavaScript " en el canal de YouTube " Freelancer for Life "

All Articles