Vanya geht zu Großmutter oder dynamischem JavaScript adaptiv

Während wir an der Adaptiven arbeiten, müssen wir ab und zu das Erscheinungsbild von Objekten ändern. Normalerweise reicht es aus, mehrere Medienabfragen zu registrieren, und wir können dies tun. In einigen Fällen müssen wir die Reihenfolge der Elemente ändern, und hier hilft beispielsweise die order-Eigenschaft der Flexbox- oder Grid-Technologie. Aber es gibt Zeiten, in denen uns das nicht hilft.

Die Idee der dynamischen Anpassung soll das Leben eines Layout-Designers vereinfachen und komplexe Layout-Elemente schnell anpassen.


Schauen Sie sich zum Beispiel diesen Fall an. Hier haben wir folgende Struktur: Überschrift und Text.



Mit einer bestimmten Erlaubnis gab der Designer jedoch an, dass sich der Text unter der Überschrift nun an einer völlig anderen Stelle in der Struktur befinden sollte.



In der Regel haben Layoutdesigner dieses Problem auf zwei Arten gelöst. Ich werde Sie schnell daran erinnern.

Ich werde es als Beispiel für eine Lebenssituation zeigen, in der der Stadtjunge Wanja wirklich für den Sommer zu seiner Großmutter kommen wollte. Aber nicht, weil er seine Großmutter vermisst hatte, sondern weil er mit dem Mädchen Olya zum Fluss gehen wollte. Und er wollte dies mit einer Auflösung unter 992px tun.



Wie werden wir das Problem von Vani lösen?
Wir können Folgendes tun: Verstecke Wanja mit dieser Erlaubnis in der Stadt und zeige Wanja mit Olya auf dem Fluss. Aber dafür müssen wir einen Klon von Vani machen.
Hier ist die Stadt (original) Wanja. Aber Wanja ist tatsächlich schon mit Olya am Fluss.



Aber zunächst ist dieser Fluss Wanja in CSS versteckt. Wir schreiben eine Medienabfrage mit den von uns benötigten Parametern, in der wir die neue Wanja zeigen und die alte Wanja (Stadt) verstecken.



Für uns hat alles geklappt - mit der Erlaubnis, die wir brauchen, befindet sich Vanya unter Olya.



Dementsprechend kehrt Wanja sofort in die Stadt zurück, wenn die Auflösung größer als 992 Pixel ist.



Und alles scheint in Ordnung zu sein, Wanja mit Olya, wenn es gebraucht wird. Sie können zerstreuen ...

Aber nicht so einfach. In der Tat sind einige Kunden sehr gegen doppelte Inhalte. Dies wird durch einige Probleme mit SEO und im Prinzip durch die Schaffung unnötiger Blöcke verursacht.

Jetzt haben wir Glück, in unserer Situation ist Wanja ein bisschen klein, und Wanja ist zufällig ein sehr großer, massiver, aufgepumpter Typ, der jeweils viele Codezeilen benötigt. In dieser Situation, einschließlich der Kunden, werden Sie aufgefordert, das DOM-Objekt mithilfe von JavaScript zu verschieben.

Dies kann ungefähr wie folgt erfolgen. Zunächst muss ich Vanya, einen Klon, in HTML entfernen und Vanya original lassen. Kommentieren Sie in CSS alle Zeilen aus, die wir ursprünglich für eine andere Umbruchoption hatten. Und fügen Sie hier einen so einfachen JS-Code hinzu.

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

Zuerst deklarieren wir die Variablen, bei denen wir die Stadt, den Fluss und das ursprüngliche Vani „aufrufen“. Als nächstes beginnen wir, die Breite des Bildschirms zu „hören“, um den Moment zu erfassen, in dem wir Wanja bewegen müssen. Nachdem wir die Breite erhalten und die Bedingung festgelegt haben, in der wir überprüfen, ob die Breite kleiner oder gleich 992px ist. Wenn wir Wanja bewegen, weisen wir ihm eine Klasse zu, damit wir prüfen, ob eine Klasse vorhanden ist, damit diese Bewegung nicht immer stattfindet (Wanja hat sich einmal bewegt und das ist es, Sie müssen sie nicht ständig übertragen). Und wir führen den Transfer innerhalb des Elternteils (zum Fluss) durch. Hier geben wir die Position im DOM-Baum an, an der wir Wanja bewegen müssen (unter Olya, über Olya). Und wenn wir eine Auflösung von mehr als 992px haben, müssen wir Wanja an den Ort zurückbringen.

Wir versuchen zu sehen, dass unsere Wanja wieder unter Olya gezogen ist.

Und alles ist in Ordnung: Wir haben keine doppelten Inhalte, alles funktioniert gut, der Kunde ist zufrieden, alle sind glücklich. Diese Methode weist jedoch eine Reihe von Problemen auf, die mit dem Schreiben von js-Code selbst verbunden sind.
Wir verstehen, dass dies für einen Einzelfall normal ist - wir haben gut geschrieben. Aber ich hatte die Idee, diesen Prozess zu automatisieren. Angenommen, ich muss 5-6 völlig unterschiedliche Elemente auf die Seite werfen. Für diese Aufgabe ist das Schreiben eines js-Codes mit dieser Methode ziemlich mühsam und zeitaufwändig. Und der zweite Punkt, der natürlich viele von Ihnen belastet hat, ist die Belastung des PCs des Benutzers, weil hier „hören“ wir ständig auf die Breite des Bildschirms. Ich wollte diesen Moment auch optimieren. Es kann auch in diesem Code optimiert werden, aber da ich ein Automatisierungstool geschrieben habe , das ich "dynamisch adaptiv" nannte , habe ich es dort optimiert.

Jetzt werde ich Ihnen zeigen, wie alles funktioniert.

Nachdem ich mein dynamisches adaptives Gerät verbunden habe, muss ich nur mit der HTML-Datei arbeiten und muss nicht auf JavaScript zugreifen. Dazu muss ich das data-da-Attribut schreiben (da steht für Dynamic Adaptive) und drei Parameter in Anführungszeichen setzen, die durch Kommas getrennt sind.
Der erste Parameter ist wo. Ich nehme eine einzigartige Klasse des Objekts, in das ich Wanja bewegen möchte.
Der zweite Parameter ist welcher (in einer Reihe). Mein Objekt hat zwei Elemente: Null und das erste (wir wissen, dass in JavaScript alles von vorne beginnt). Ich muss Wanja nach dem Objekt "1" einfügen, also schreibe ich den Parameter "2".

Der dritte Parameter ist wann. Im dritten Parameter schreibe ich "992", dies ist die Auflösung, unter der Wanja wirklich nach Olya gelangen möchte.

Der zweite und dritte Parameter sind optional. Standardmäßig wird das Objekt mit einer Auflösung unter 768px an das Ende des Blocks verschoben.



Wir überprüfen. Alles funktioniert wie in früheren Versionen, jedoch ohne Inhaltsduplizierung und alles ist automatisiert.

Betrachten Sie Annehmlichkeiten.

Um Vanya über Olya zu bewegen, muss ich nur die Seriennummer im zweiten Attributparameter ändern. Und Wanja wird schon über Olya sein.



Praktisch, richtig? Ich kann das gleiche mit dem dritten Haltepunktparameter tun. Ich kann zum Beispiel 1280px angeben. (data-da = "content__column_river, 1,1280"). Also wird Wanja etwas früher nach Olya gehen, genau dann, wenn dieser Wert erreicht ist. Und das Coolste ist, dass Wanja absolut automatisch an seinen Platz zurückkehrt.

Für zusätzlichen Komfort können Sie anstelle der Seriennummer im zweiten Parameter des Attributs einen von zwei Werten angeben - den ersten oder den letzten. Wenn ich zuerst schreibe, bewegt sich Wanja über die Überschrift "Ich bin ein Fluss". Es befindet sich als erstes im Objekt, an das wir es senden. Wenn Sie also zuletzt angeben, befindet sich unsere Wanja ganz unten im Objekt.



Schauen wir uns noch ein paar Beispiele an. Angenommen, wir können Kolya in den Garten der Lota schicken und Anya auch nach Van und Ola zum Fluss schicken, es wird mehr Spaß machen. Wir schreiben für Kolya ein Attribut mit den Parametern
data-da = "content__column_garden, 2.992" . Und für Ani schreiben wir data-da = "content__column_river, 1.1280".

Wir schauen: Anya, Olya und Vanya am Fluss. Kolya geht etwas später nach Lota. Bei der Rückkehr sind alle Jungs an ihren Plätzen. Alles ist ganz toll.

Spielen Sie in CodePen herum: codepen.io/FreelancerLifeStyle/project/full/ZmWOPm

Natürlich fragen Sie sich vielleicht : Was ist mit dem Ereignis, das an verschiebbaren Objekten hängen bleiben könnte? Wenn wir zum Beispiel zunächst auf Anya klicken, erhalten wir ein Fenster mit der Aufschrift: "Alles ist in Ordnung." Mal sehen, was passiert, wenn sie mit den Jungs zum Fluss zieht.
Bewegen, klicken - alles funktioniert einwandfrei, das Fenster wird ebenfalls angezeigt. Das Ereignis bleibt beim Verschieben beim Objekt.



Und die zweite spannende Frage ist die Belastung, die ich zu minimieren versucht habe. Jetzt werde ich dies zeigen, indem ich ein wenig über den Code erzähle, der unter der Haube des dynamischen Adaptiven verborgen ist.

Zunächst deklariere ich einige Variablen, einschließlich der Erstellung von Arrays, die ich unten ausfülle. Ich erhalte das Attribut, trenne es und verwandle es in ein Array, um einzelne Elemente zu erhalten. Als nächstes fülle ich die ursprüngliche Position der Objekte aus, damit sie an den Platz ihrer Eltern (in der Stadt) zurückgebracht werden können. Danach fülle ich das Array der Elemente selbst aus, damit ich es für den korrekten Betrieb sortieren kann.

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

Und am Ende wird die Optimierung implementiert, über die ich gesprochen habe: Anstelle der Fenstergrößenänderung (jede Fensteränderung abhören) hören wir Änderungen nur am Haltepunkt mit window matchMedia. Auf diese Weise erhalten wir die Browserantwort nur an einigen Stellen: denjenigen, die wir in den Attributen unserer Objekte angegeben haben.

Als nächstes sammle ich ein Array dieser Haltepunkte und hänge ein Ereignis für jeden Haltepunkt auf. In der Hauptfunktion dynamic_adapt erweitere ich all dies innerhalb des Arrays, das wir gesammelt und sortiert haben, überprüfe das Vorhandensein eines Haltepunkts, verschiebe die Elemente oder bringe sie an ihren Platz zurück.

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

Zurück zur Ausgangsposition:

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

Natürlich haben viele eine Frage zu Mobile First. Es gibt keine Probleme - wir müssen nur einige Variablen im Code ändern (das Entfernen dieser Einstellung in HTML funktioniert bereits):

  1. Wenn ich ein Array von Haltepunkten sammle, müssen wir hier "max" in "min" ändern. Und das Ereignis wird nicht nach maximaler, sondern nach minimaler Breite „angehört“.
  2. Sie müssen auch die Sortierung ändern, damit die Objekte im Array in einer anderen Reihenfolge ausgerichtet werden, wenn wir sie nach Haltepunkten sortieren.

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

Auch in Dynamic Adaptive verwende ich eine bestimmte Methode, um den Index zu erhalten, wenn das Objekt geworfen wird. Und hier sind die ersten Mängel - ich muss das übertragene Objekt ausschließen. Jene. Wenn ich Objekte an denselben Ort übertrage, damit alles angemessen ist und die Objekte in der gewünschten Reihenfolge ausgerichtet sind, muss ich beim Übertragen das zuvor übertragene Objekt an denselben Punkt ausschließen.

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

Aber bringt ein anderes Sortierproblem mit sich. Wenn ich zum Beispiel die Jungs gleichzeitig auf die gleiche Position bringe, kann ich ihre Reihenfolge nicht kontrollieren.

Eine Möglichkeit, diese dynamische Anpassung zu entwickeln, kann beispielsweise die Erstellung mehrerer Haltepunkte sein. Beispiel: Wir müssen Wanja mit 1280 Pixel nach Ola und mit 992 Pixel Wanja nach Ola bringen, um in den Garten zu ziehen oder an den Ort zurückzukehren. Diese Funktionalität ist derzeit nicht verfügbar, daher gibt es noch viel zu tun. Um 95% der alltäglichen Aufgaben im Adaptive abzudecken, reicht mir jetzt die Funktionalität, die jetzt ausreicht. Um das Problem des Verschiebens von Objekten an einen anderen Ort zu lösen, muss ich jetzt einige Sekunden damit verbringen, ein Attribut zu schreiben. Und ich werde das Ergebnis bekommen, das ich brauche.

Natürlich fordere ich absolut jeden, der die dynamische Anpassung mochte, auf, an deren Prüfung, Entwicklung und Verbesserung sowohl der Funktionalität als auch der Optimierung des Codes selbst teilzunehmen. Und das alles, damit unsere Kohl, Ani, Vani und Olya zusammen sind, wenn sie es wollen.

GitHub-Repository - github.com/FreelancerLifeStyle/dynamic_adapt

Basierend auf dem Video " Dynamic Adaptive // ​​Schnelle Anpassung komplexer Objekte in JavaScript " auf dem Youtube-Kanal " Freelancer for Life "

All Articles