Vanya去找祖母或动态JavaScript自适应

在进行自适应工作时,我们时不时必须更改对象的外观。通常,我们只要注册几个媒体查询就可以了。在某些情况下,我们需要更改元素的顺序,例如,这里有来自Flexbox或Grid技术的order属性。但是有时候这对我们没有帮助。

动态自适应的思想旨在简化布局设计者的工作,并快速适应复杂的布局元素。


例如,看这种情况。在这里,我们具有以下结构:标题和一些文本。



但是,在获得一定许可的情况下,设计人员指出,标题下的文本现在应该在结构中完全不同的位置。



通常,布局设计人员可以通过以下两种方法之一解决此问题。我会很快提醒您的。

我将以一个生活状况的例子为例进行说明,当时男生Vanya确实想在夏天到祖母那里来。但这不是因为他想念祖母,而是因为他想和女孩Olya一起去河里。他想以低于992px的分辨率进行操作。



我们将如何解决Vani问题?
我们可以执行以下操作:在获得许可的情况下将Vanya隐藏在城市中,并与Olya在河上展示Vanya。但是为此,我们需要克隆Vani。
这是城市(原始)瓦尼亚。但是事实上,瓦尼亚(Vanya)已经和奥利亚(Olya)在河上了。



但是最初,这条河Vanya隐藏在CSS中。我们用所需的参数编写媒体查询,在其中显示新的Vanya,隐藏旧的Vanya(城市)。



一切都为我们解决了-在我们需要的许可下,Vanya陷入Olya的统治之下。



因此,当分辨率大于992px时,Vanya立即返回城市。



一切似乎都还好,需要时Vanya和Olya在一起。你可以驱散...

但不是那么简单。实际上,一些客户非常反对重复的内容。这是由SEO的一些问题以及原则上不必要的块的创建引起的。

现在我们很幸运,在我们的情况下,Vanya有点小,而Vanya却又很大,很笨重,需要很多代码。在这种情况下,包括客户在内,他们都要求您使用JavaScript移动DOM对象。

这可以大致如下进行。首先,我必须从HTML中删除克隆的Vanya,并将Vanya保留为原始格式。在CSS中,注释掉我们最初用于其他包装选项的所有行。并在此处添加这样一个简单的JS代码。

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

首先,我们声明变量以“拨号”到城市,河流和原始瓦尼。接下来,我们开始“监听”屏幕的宽度,以捕捉需要移动Vanya的时刻。得到宽度并设置条件后,我们检查宽度是否小于或等于992px。搬迁Vanya时,我们为他分配了一个班级,因此我们检查班级是否存在,以确保这种迁移不会一直发生(Vanya搬了一次,就是这样,您不需要不断地转移它)。然后,我们在父级(到河流)内部执行转移,在这里,我们指出了需要移动Vanya(在Olya之下,Olya之上)在DOM树中的位置。当分辨率大于992px时,必须将Vanya返回该位置。

我们尝试看到我们的Vanya再次移居Olya之下。

一切都很好:我们没有重复的内容,一切都很好,客户满意,每个人都很高兴。但是,此方法存在许多与js代码本身的编写相关的问题。
我们了解在单个情况下这是正常现象-我们写得很好。但是我想到了使这个过程自动化的想法。假设我需要在页面上抛出5-6个完全不同的元素。对于此任务,使用此方法编写js代码非常麻烦且耗时。第二点自然使许多人感到压力,这是用户PC上的负载,因为在这里,我们一直在“监听”屏幕的宽度。我也想优化这一刻。也可以在此代码中对其进行优化,但是由于我编写了一个称为“动态自适应”的自动化工具,因此我在此处进行了优化。

现在,我将向您展示一切。

连接了动态自适应系统后,我只需要使用HTML文件,就不需要访问JavaScript。为此,我需要编写data-da属性(da是动态自适应的缩写),并在引号中指定三个参数,并用逗号分隔。
第一个参数是where。我将要移动Vanya的对象放在独特的类中。
第二个参数是(连续)。我的对象有两个元素:零和第一个(我们知道在JavaScript中,一切都是从头开始的)。我需要在“ 1”对象之后插入Vanya,因此我正在编写“ 2”参数。

第三个参数是when。在第三个参数中,我输入“ 992”,这是Vanya真正想要达到Olya的分辨率。

第二和第三个参数是可选的。默认情况下,对象将以低于768px的分辨率移动到块的末尾。



我们检查。一切工作与以前的版本相同,但是没有内容重复并且一切都是自动化的。

考虑设施。

现在,为了将Vanya移到Olya上,我只需要更改第二个属性参数中的序列号即可。 Vanya已经超过Olya。



方便吧?我可以对第三个断点参数执行相同的操作。我可以指定例如1280px。 (data-da =“ content__column_river,1,1280”)。因此,恰好达到此值时,Vanya会更早到达Olya。最酷的是Vanya绝对会自动回到他的位置。

为了增加舒适度,您可以指定两个值之一-第一个或最后一个,以代替属性的第二个参数中的序列号。当我第一次写时,Vanya将移至标题“我是河”。它将位于我们发送对象的内部的第一个位置。因此,如果您指定last,那么我们的Vanya将位于对象的最底部。



让我们再看几个例子。假设我们可以将Kolya发送到Lota的花园,也可以将Anya发送到河中的Van和Ola,这将变得更加有趣。我们为Kolya编写了一个参数为
data-da =“ content__column_garden,2,992”的属性。对于Ani,我们编写data-da =“ content__column_river,1.1280”。

我们看:河上的Anya,Olya和Vanya。 Kolya过了一会儿去了Lota。返回时,所有的人都在他们的地方。一切都很棒。

在CodePen中玩: codepen.io/FreelancerLifeStyle/project/full/ZmWOPm

当然,您可能会遇到一个问题:可挂在可重定位对象上的事件呢?例如,最初,当单击Anya时,我们将获得一个标有“一切都OK”的窗口。让我们看看当她和家伙们一起搬到河边时会发生什么。
移动,单击-一切正常,还显示窗口。事件在移动时仍与对象一起保留。



第二个令人兴奋的问题是负载,我试图将其最小化。现在,我将通过讲一些隐藏在动态自适应引擎背后的代码来展示这一点。

首先,我声明一些变量,包括创建数组,这些变量我在下面填写。我得到属性,将其分离,将其变成数组以获取单个元素。接下来,我填写对象的原始位置,以便可以将其返回到父母的位置(在城市中)。之后,我填写元素本身的数组,以便可以对其进行排序以进行正确的操作。

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

最后,实现了我所讨论的优化:不是使用窗口调整大小(监听每个窗口更改),而是仅使用窗口matchMedia监听断点处的更改。这样,我们仅在某些时候获得浏览器响应:在对象属性中指示的响应。

接下来,我收集这些断点的数组,并为每个断点挂起一个事件。在主要的dynamic_adapt函数中,我将所有这些扩展到我们收集和排序的数组内,检查是否存在断点,移动元素或将其返回到其位置。

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

返回原位功能:

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

自然,许多人都对Mobile First有疑问。没问题-我们只需要更改代码中的一些变量即可(在HTML中删除此设置已在起作用):

  1. 当我收集一组断点时,我们需要在此处将“ max”更改为“ min”。事件将不是通过最大宽度而是最小宽度来“监听”。
  2. 您还需要更改排序,以便在我们按断点对它们进行排序时,数组中的对象以不同的顺序对齐。

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

同样在动态自适应中,当对象被抛出时,我使用某种获取索引的方法。这是第一个缺陷-我必须排除传输的对象。那些。当我将对象转移到同一位置时,以便一切都足够,并且对象按我们想要的顺序排列,转移时,我需要将先前转移的对象排除到同一点。

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

但是带来了另一个分类问题。例如,如果我将这些人同时转移到同一位置,则将无法控制他们的订单。

开发这种动态自适应方法的一种方法可以是,例如,创建多个断点。例如,我们需要将Vanya移至1280px处的Ola,然后将Vanya移至992px处才能移至花园或返回该位置。该功能现在不可用,因此仍有工作要做。通常,要满足95%的自适应日常任务,现在的功能对我来说已经足够了。为了解决将项目移动到另一个地方的问题,我现在需要花几秒钟编写一个属性。我会得到所需的结果。

自然,我强烈要求所有喜欢动态自适应的人参与其功能,代码本身的优化的测试,开发和改进。所有这些使我们的Kohl,Ani,Vani和Olya可以在需要的时候在一起。

库GitHub的上 - github.com/FreelancerLifeStyle/dynamic_adapt

基于视频“ 动态自适应//在JavaScript复杂对象的快速自适应的YouTube频道”“ 自由职业者的生活

All Articles