Vanya vai para a avó ou JavaScript dinâmico adaptável

Enquanto trabalhamos no adaptável, precisamos agora e depois mudar a aparência dos objetos. Normalmente, basta registrar várias consultas de mídia e podemos fazê-lo. Em alguns casos, precisamos alterar a ordem dos elementos e aqui, por exemplo, a propriedade de pedido da tecnologia Flexbox ou Grid vem em socorro. Mas há momentos em que isso não nos ajuda.

A idéia de adaptação dinâmica foi projetada para simplificar a vida de um designer de layout e adaptar rapidamente elementos de layout complexos.


Por exemplo, veja este caso. Aqui temos a seguinte estrutura: cabeçalho e algum texto.



Mas com uma certa permissão, o designer indicou que o texto que estava sob o cabeçalho agora deveria estar em um local completamente diferente na estrutura.



Normalmente, os designers de layout resolviam esse problema de duas maneiras. Vou lembrá-lo rapidamente deles.

Vou mostrá-lo como um exemplo de uma situação de vida em que o garoto da cidade Vanya realmente queria ir à avó durante o verão. Mas não porque sentia falta da avó, mas porque queria ir ao rio com a menina Olya. E ele queria fazer isso em uma resolução abaixo de 992px.



Como vamos resolver o problema de Vani?
Podemos fazer o seguinte: Esconda Vanya na cidade com essa permissão e mostre Vanya no rio com Olya. Mas, para isso, precisamos criar um clone de Vani.
Aqui está a cidade (original) Vanya. Mas Vanya, de fato, já está no rio com Olya.



Mas inicialmente este rio Vanya está escondido em CSS. Escrevemos uma consulta de mídia com os parâmetros que precisamos, nos quais mostramos o novo Vanya e ocultamos o antigo Vanya (cidade).



Tudo deu certo para nós - com a permissão de que precisamos, Vanya se vê sob Olya.



Assim, quando a resolução é maior que 992px, Vanya retorna imediatamente à cidade.



E tudo parece estar bem, Vanya com Olya quando é necessário. Você pode dispersar ...

Mas não é tão simples. De fato, alguns clientes são muito contrários à duplicação de conteúdo. Isso é causado por alguns problemas com o SEO e, em princípio, pela criação de blocos desnecessários.

Agora temos sorte, em nossa situação, Vanya é meio pequeno, e Vanya é um cara muito grande, volumoso e empolgado, que exige, respectivamente, muitas linhas de código. E nessa situação, incluindo clientes, eles solicitam que você mova o objeto DOM usando JavaScript.

Isso pode ser feito aproximadamente da seguinte maneira. Para começar, tenho que remover Vanya, que é um clone, em HTML e deixar o Vanya original. Em CSS, comente todas as linhas que tínhamos originalmente para outra opção de agrupamento. E adicione aqui um código JS tão simples.

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

Primeiro, declaramos as variáveis ​​em que “discamos” para a cidade, rio e Vani original. Em seguida, começamos a "ouvir" a largura da tela para capturar o momento em que precisamos mover Vanya. Depois de obtermos a largura e definirmos a condição em que verificamos que a largura é menor ou igual a 992px. Ao mover Vanya, atribuímos uma classe a ele, então verificamos a presença de uma classe para que esse movimento não aconteça o tempo todo (Vanya se moveu uma vez e é isso, você não precisa transferi-lo constantemente). E realizamos a transferência dentro do pai (para o rio), aqui indicamos a posição na árvore DOM onde precisamos mover Vanya (sob Olya, acima de Olya). E quando tivermos uma resolução maior que 992px, devemos devolver Vanya ao local.

Tentamos ver que nosso Vanya novamente se moveu sob Olya.

E está tudo bem: não temos conteúdo duplicado, tudo funciona bem, o cliente está satisfeito, todo mundo está feliz. Mas esse método tem vários problemas associados à escrita do próprio código js.
Entendemos que, para um único caso, isso é normal - escrevemos bem. Mas tive a ideia de automatizar esse processo. Suponha que eu precise lançar 5-6 elementos completamente diferentes na página. Para esta tarefa, escrever um código js usando esse método é bastante trabalhoso e demorado. E o segundo ponto, que naturalmente sobrecarregou muitos de vocês, é a carga no PC do usuário, porque aqui estamos constantemente "ouvindo" a largura da tela. Eu também queria otimizar esse momento. Ele também pode ser otimizado nesse código, mas como escrevi uma ferramenta de automação que chamei de "adaptável dinâmico" , otimizei lá.

Agora vou mostrar como tudo funciona.

Depois de conectar minha adaptativa dinâmica, só preciso trabalhar com o arquivo HTML e não há necessidade de acessar o JavaScript. Para fazer isso, preciso escrever o atributo data-da (da é a abreviação de adaptativo dinâmico) e especificar três parâmetros entre aspas, separados por vírgulas.
O primeiro parâmetro é onde. Tomo uma classe única do objeto para onde quero mover Vanya.
O segundo parâmetro é qual (em uma linha). Meu objeto tem dois elementos: zero e o primeiro (sabemos que em JavaScript tudo começa do zero). Eu preciso inserir Vanya após o objeto "1", então estou escrevendo o parâmetro "2".

O terceiro parâmetro é quando. No terceiro parâmetro, escrevo "992", esta é a resolução abaixo da qual Vanya realmente quer chegar a Olya.

O segundo e terceiro parâmetros são opcionais. Por padrão, o objeto será movido para o final do bloco em uma resolução abaixo de 768px.



Nós verificamos. Tudo funciona da mesma forma que nas versões anteriores, mas sem duplicação de conteúdo e tudo é automatizado.

Considere as comodidades.

Agora, para mover Vanya sobre Olya, só preciso alterar o número de série no segundo parâmetro de atributo. E Vanya já estará sobre Olya.



Conveniente, certo? Eu posso fazer o mesmo com o terceiro parâmetro de ponto de interrupção. Eu posso especificar, por exemplo, 1280px. (data-da = "content__column_river, 1.1280"). Então Vanya irá para Olya um pouco antes, exatamente quando esse valor for atingido. E o mais legal é que Vanya retornará ao seu lugar absolutamente automaticamente.

Para maior conforto, em vez do número de série no segundo parâmetro do atributo, você pode especificar um dos dois valores - primeiro ou último. Quando escrevo primeiro, Vanya passa pelo cabeçalho "Eu sou um rio". Ele será localizado o primeiro dentro do objeto para onde o enviamos. Portanto, se você especificar last, nosso Vanya estará na parte inferior do objeto.



Vejamos mais alguns exemplos. Suponha que possamos enviar Kolya para o jardim da lota e enviar Anya para o rio para Van e Ola também, será mais divertido. Escrevemos para Kolya um atributo com os parâmetros
data-da = "content__column_garden, 2.992" . E para Ani, escrevemos data-da = “content__column_river, 1.1280”.

Observamos: Anya, Olya e Vanya no rio. Kolya vai para Lota um pouco mais tarde. Ao voltar, todos os caras estão em seus lugares. Tudo é bom.

Brinque no CodePen: codepen.io/FreelancerLifeStyle/project/full/ZmWOPm

Obviamente, você pode ter uma pergunta: e o evento que pode ser pendurado em objetos realocáveis? Por exemplo, inicialmente, ao clicar em Anya, obtemos uma janela com a inscrição: "Está tudo bem". Vamos ver o que acontece quando ela se move com os caras para o rio.
Mova-se, clique - tudo funciona bem, a janela também é exibida. O evento permanece com o objeto quando ele é movido.



E a segunda pergunta emocionante é a carga, que tentei minimizar. Agora vou mostrar isso falando um pouco sobre o código que está oculto sob o capô da dinâmica adaptativa.

Antes de tudo, declaro algumas variáveis, incluindo a criação de matrizes, que preencho abaixo. Pego o atributo, separo-o, transformando-o em uma matriz para obter elementos individuais. Em seguida, preencheu a posição original dos objetos para que possam ser devolvidos ao local dos pais (na cidade). Depois disso, preencho a matriz dos próprios elementos para poder classificá-la para a operação correta.

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

E, no final, a otimização de que falei é implementada: em vez de redimensionar a janela (ouvir todas as alterações de janela), apenas ouviremos as alterações no ponto de interrupção usando a janela matchMedia. Dessa forma, obtemos a resposta do navegador apenas em alguns pontos: aqueles que indicamos nos atributos de nossos objetos.

Em seguida, coleciono uma matriz desses pontos de interrupção e penduro um evento para cada ponto de interrupção. Na principal função dynamic_adapt, expanda tudo isso dentro da matriz que coletamos e classificamos, verifico a presença de um ponto de interrupção, movo os elementos ou os retorno ao seu 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();
}

Retorne à função posição inicial:

//   
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, muitos têm uma pergunta sobre o Mobile First. Não há problemas - precisamos alterar apenas algumas variáveis ​​no código (a remoção dessa configuração no HTML já está funcionando):

  1. Quando coleto uma matriz de pontos de interrupção, precisamos alterar "max" para "min" aqui. E o evento será “escutado” não pela largura máxima, mas pela largura mínima.
  2. Você também precisa alterar a classificação para que os objetos na matriz sejam alinhados em uma ordem diferente quando os classificarmos por ponto de interrupção.

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

Também na adaptação dinâmica, uso um determinado método para obter o índice quando o objeto é lançado. E aqui estão as primeiras falhas - eu tenho que excluir o objeto transferido. Essa. quando transfiro objetos para o mesmo local, para que tudo esteja adequado e os objetos se alinhem na ordem em que queremos, ao transferir, preciso excluir o objeto transferido anteriormente para o mesmo ponto.

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

Mas envolve outro problema de classificação. Por exemplo, se eu transferir os caras ao mesmo tempo para a mesma posição, não poderei controlar a ordem deles.

Uma das maneiras de desenvolver essa dinâmica adaptativa pode ser, por exemplo, a criação de vários pontos de interrupção. Por exemplo, precisamos mover Vanya para Ola em 1280px e em 992px Vanya para ir para o jardim ou retornar ao local. Essa funcionalidade não está disponível agora, portanto ainda há trabalho a ser feito. Em geral, para cobrir 95% das tarefas diárias no adaptativo, a funcionalidade que é agora é suficiente para mim. Para resolver o problema de mover itens para outro local, preciso gastar agora alguns segundos escrevendo um atributo. E vou obter o resultado que preciso.

Naturalmente, exorto absolutamente todos os que gostaram da dinâmica adaptativa a participar de seus testes, desenvolvimento e aprimoramento tanto da funcionalidade quanto da otimização do próprio código. E tudo para que nossos Kohl, Ani, Vani e Olya estivessem juntos quando quisessem.

Repositório no GitHub - github.com/FreelancerLifeStyle/dynamic_adapt

Baseado no vídeo " Adaptativo Dinâmico // Adaptação Rápida de Objetos Complexos em JavaScript " no canal do youtube " Freelancer for Life "

All Articles