Vanya va à grand-mère ou adaptative JavaScript dynamique

Tout en travaillant sur l'adaptatif, nous devons de temps en temps changer l'apparence des objets. Habituellement, il nous suffit d'enregistrer plusieurs requêtes médiatiques et nous pouvons le faire. Dans certains cas, nous devons changer l'ordre des éléments, et ici, par exemple, la propriété order de Flexbox ou de la technologie Grid vient à la rescousse. Mais il y a des moments où cela ne nous aide pas.

L'idée de l'adaptabilité dynamique est conçue pour simplifier la vie d'un concepteur de mise en page et adapter rapidement des éléments de mise en page complexes.


Par exemple, regardez ce cas. Ici, nous avons la structure suivante: en-tête et du texte.



Mais avec une certaine permission, le concepteur a indiqué que le texte qui était sous le titre devrait maintenant être à un endroit complètement différent dans la structure.



En règle générale, les concepteurs de mise en page ont résolu ce problème de deux manières. Je vais leur rappeler rapidement.

Je vais le montrer comme un exemple d'une situation de vie où le garçon de la ville Vanya voulait vraiment venir chez sa grand-mère pour l'été. Mais pas parce qu'il a raté sa grand-mère, mais parce qu'il voulait aller à la rivière avec la fille Olya. Et il voulait le faire avec une résolution inférieure à 992px.



Comment allons-nous résoudre le problème de Vani?
Nous pouvons faire ce qui suit: cacher Vanya dans la ville avec cette permission et montrer Vanya sur la rivière avec Olya. Mais pour cela, nous devons faire un clone de Vani.
Voici la ville (originale) Vanya. Mais Vanya, en fait, sur la rivière déjà avec Olya.



Mais au départ, cette rivière Vanya est cachée dans CSS. Nous écrivons une requête média avec les paramètres dont nous avons besoin, dans laquelle nous montrons la nouvelle Vanya et cachons l'ancienne Vanya (ville).



Tout a fonctionné pour nous - avec la permission dont nous avons besoin, Vanya se retrouve sous Olya.



En conséquence, lorsque la résolution est supérieure à 992 pixels, Vanya revient immédiatement dans la ville.



Et tout semble aller bien, Vanya avec Olya quand c'est nécessaire. Vous pouvez vous disperser ...

Mais pas si simple. En fait, certains clients sont très opposés au contenu en double. Cela est dû à certains problèmes de référencement et, en principe, à la création de blocs inutiles.

Maintenant, nous avons de la chance, dans notre situation, Vanya est un peu petite, et Vanya se trouve être des gars très gros, massifs et gonflés, qui nécessitent respectivement beaucoup de lignes de code. Et dans cette situation, y compris les clients, ils vous demandent de déplacer l'objet DOM à l'aide de JavaScript.

Cela peut être fait approximativement comme suit. Pour commencer, je dois supprimer Vanya, qui est un clone, en HTML et laisser Vanya originale. En CSS, commentez toutes les lignes que nous avions à l'origine pour une autre option d'habillage. Et ajoutez ici un code JS aussi 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();

Tout d'abord, nous déclarons les variables où nous «composons» la ville, la rivière et le Vani d'origine. Ensuite, nous commençons à «écouter» la largeur de l'écran afin de saisir l'instant où nous devons déplacer Vanya. Après avoir obtenu la largeur et défini la condition dans laquelle nous vérifions que la largeur est inférieure ou égale à 992px. Lors du déplacement de Vanya, nous lui attribuons une classe, nous vérifions donc la présence d'une classe afin que ce mouvement ne se produise pas tout le temps (Vanya a bougé une fois et c'est tout, vous n'avez pas besoin de la transférer constamment). Et nous effectuons le transfert lui-même à l'intérieur du parent (vers la rivière), ici nous indiquons la position dans l'arbre DOM où nous devons déplacer Vanya (sous Olya, au-dessus d'Olya). Et quand nous avons une résolution supérieure à 992px, nous devons retourner Vanya à l'endroit.

Nous essayons de voir que notre Vanya s'est de nouveau déplacée sous Olya.

Et tout va bien: nous n’avons aucun contenu en double, tout fonctionne bien, le client est satisfait, tout le monde est content. Mais cette méthode a un certain nombre de problèmes associés à l'écriture du code js lui-même.
Nous comprenons que pour un seul cas, c'est normal - nous avons bien écrit. Mais j'ai eu l'idée d'automatiser ce processus. Supposons que je doive lancer 5-6 éléments complètement différents sur la page. Pour cette tâche, l'écriture d'un code js à l'aide de cette méthode est assez laborieuse et prend du temps. Et le deuxième point, qui, naturellement, a pesé sur beaucoup d'entre vous, est la charge sur le PC de l'utilisateur, car ici, nous «écoutons» constamment la largeur de l'écran. Je voulais aussi optimiser ce moment. Il peut également être optimisé dans ce code, mais depuis que j'ai écrit un outil d' automatisation que j'ai appelé «adaptatif dynamique» , je l'ai optimisé là-bas.

Je vais maintenant vous montrer comment tout cela fonctionne.

Après avoir connecté mon adaptative dynamique, je n'ai qu'à travailler avec le fichier HTML et il n'est pas nécessaire d'accéder à JavaScript. Pour ce faire, je dois écrire l'attribut data-da (da est l'abréviation de dynamic adaptive) et spécifier trois paramètres entre guillemets, séparés par des virgules.
Le premier paramètre est où. Je prends une classe unique de l'objet où je veux déplacer Vanya.
Le deuxième paramètre est lequel (dans une rangée). Mon objet a deux éléments: zéro et le premier (on sait qu'en JavaScript tout part de zéro). J'ai besoin d'insérer Vanya après l'objet "1", donc j'écris le paramètre "2".

Le troisième paramètre est quand. Dans le troisième paramètre, j'écris «992», c'est la résolution en dessous de laquelle Vanya veut vraiment arriver à Olya.

Les deuxième et troisième paramètres sont facultatifs. Par défaut, l'objet sera déplacé à la fin du bloc à une résolution inférieure à 768 px.



Nous vérifions. Tout fonctionne de la même manière que dans les versions précédentes, mais sans duplication de contenu et tout est automatisé.

Pensez aux commodités.

Maintenant, afin de déplacer Vanya sur Olya, j'ai juste besoin de changer le numéro de série dans le deuxième paramètre d'attribut. Et Vanya sera déjà sur Olya.



Pratique, non? Je peux faire de même avec le troisième paramètre de point d'arrêt. Je peux spécifier, par exemple, 1280px. (data-da = "content__column_river, 1,1280"). Vanya ira donc à Olya un peu plus tôt, précisément lorsque cette valeur sera atteinte. Et le plus cool, c'est que Vanya retournera chez lui de manière absolument automatique.

Pour plus de confort, au lieu du numéro de série dans le deuxième paramètre de l'attribut, vous pouvez spécifier l'une des deux valeurs - première ou dernière. Lorsque j'écrirai en premier, Vanya passera sur la rubrique "Je suis une rivière". Il sera situé le premier à l'intérieur de l'objet où nous l'envoyons. En conséquence, si vous spécifiez en dernier, alors notre Vanya sera tout en bas de l'objet.



Regardons quelques autres exemples. Supposons que nous puissions envoyer Kolya au jardin de la lota et envoyer Anya à la rivière à Van et Ola aussi, ce sera plus amusant. Nous écrivons pour Kolya un attribut avec les paramètres
data-da = "content__column_garden, 2,992" . Et pour Ani nous écrivons data-da = "content__column_river, 1.1280"

Nous regardons: Anya, Olya et Vanya sur la rivière. Kolya va à Lota un peu plus tard. Au retour, tous les gars sont à leur place. Tout est bon.

Jouez dans CodePen: codepen.io/FreelancerLifeStyle/project/full/ZmWOPm

Bien sûr, vous pouvez avoir une question: qu'en est-il de l'événement qui pourrait être suspendu à des objets déplaçables? Par exemple, au départ, en cliquant sur Anya, nous obtenons une fenêtre avec l'inscription: "Tout va bien." Voyons ce qui se passe quand elle se déplace avec les gars vers la rivière.
Déplacez, faites un clic - tout fonctionne bien, la fenêtre s'affiche également. L'événement reste avec l'objet lorsqu'il est déplacé.



Et la deuxième question passionnante est la charge, que j'ai essayé de minimiser. Maintenant, je vais le montrer en racontant un peu le code qui est caché sous le capot de l'adaptatif dynamique.

Tout d'abord, je déclare certaines variables, y compris la création de tableaux, que je remplis ci-dessous. J'obtiens l'attribut, le sépare, le transformant en un tableau pour obtenir des éléments individuels. Ensuite, je remplis la position d'origine des objets pour qu'ils puissent ensuite être ramenés chez leurs parents (en ville). Après cela, je remplis le tableau des éléments eux-mêmes, afin de pouvoir ensuite le trier pour un fonctionnement correct.

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);
	}
}

Et à la fin, l'optimisation dont j'ai parlé est implémentée: au lieu de redimensionner la fenêtre (écouter chaque changement de fenêtre), nous n'écouterons les changements au point d'arrêt qu'en utilisant window matchMedia. De cette façon, nous n'obtenons la réponse du navigateur qu'à certains points: ceux que nous avons indiqués dans les attributs de nos objets.

Ensuite, je collecte un tableau de ces points d'arrêt et suspend un événement pour chaque point d'arrêt. Dans la fonction principale dynamic_adapt, j'étends tout cela à l'intérieur du tableau que nous avons collecté et trié, vérifie la présence d'un point d'arrêt, déplace les éléments ou les remets à leur place.

// 
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();
}

Fonction de retour à la position d'origine:

//   
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]);
}

Naturellement, beaucoup ont une question sur Mobile First. Il n'y a aucun problème - nous devons changer seulement quelques variables dans le code (la suppression de ce paramètre en HTML fonctionne déjà):

  1. Lorsque je collecte un tableau de points d'arrêt, nous devons changer "max" en "min" ici. Et l'événement sera «écouté» non pas par largeur max, mais par largeur min.
  2. Vous devez également modifier le tri afin que les objets du tableau soient alignés dans un ordre différent lorsque nous les trions par point d'arrêt.

	// 
	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 }
		});
	}

Toujours en dynamique adaptative, j'utilise une certaine méthode pour obtenir l'index lorsque l'objet est lancé. Et voici les premiers défauts - je dois exclure l'objet transféré. Ceux. lorsque je transfère des objets au même endroit, afin que tout soit adéquat et que les objets s'alignent dans l'ordre que nous voulons, lors du transfert, je dois exclure l'objet précédemment transféré au même point.

//       
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;
}

Mais entraîne un autre problème de tri. Par exemple, si je transfère les gars en même temps au même poste, je ne pourrai pas contrôler leur commande.

Une des façons de développer cet adaptatif dynamique peut être, par exemple, la création de plusieurs points d'arrêt. Par exemple, nous devons déplacer Vanya à Ola à 1280 px et à 992 px Vanya pour déménager dans le jardin ou retourner à l'endroit. Cette fonctionnalité n'est pas disponible actuellement, il y a donc encore du travail à faire. En général, pour couvrir 95% des tâches quotidiennes dans l'adaptatif, la fonctionnalité qui me suffit maintenant me suffit. Pour résoudre le problème du déplacement d'objets vers un autre endroit, je dois maintenant passer quelques secondes à écrire un attribut. Et j'obtiendrai le résultat dont j'ai besoin.

Naturellement, j'exhorte absolument tous ceux qui ont aimé l'adaptabilité dynamique à participer à ses tests, à son développement et à son amélioration à la fois fonctionnelle et d'optimisation du code lui-même. Et tout pour que nos Kohl, Ani, Vani et Olya soient ensemble quand ils le veulent. Dépôt

GitHub - github.com/FreelancerLifeStyle/dynamic_adapt

Basé sur la vidéo " Dynamic Adaptive // ​​Adaptation rapide d'objets complexes en JavaScript " sur la chaîne youtube " Freelancer for Life "

All Articles