「 Vanilla JS 」监听窗口变化和元素变化

6,105 阅读1分钟

resize事件

简介

可用来监听window对象的变化

应用:根据窗口改变rem

// 基准大小
const baseSize = 41.4;
// 设置 rem 函数
function setRem(width) {
  // 当前页面宽度相对于 414 宽的缩放比例,可根据自己需要修改。
  const scale = width / 414;
  // 设置页面根节点字体大小
  document.documentElement.style.fontSize = baseSize * Math.min(scale, 2) + 'px';
}
window.addEventListener('resize', debounce(setRem));

需要注意的是resize是高频事件,可使用debounce防止多次触发

MutationObserver

简介

  • MutationObserver(callback)为构造函数,可用于实例化监听DOM节点变化的观察器
  • 出自DOM3规范
  • 实例对象拥有三个方法:
    • observe(element, options)开始观察节点,发生变化时触发回调(先放到消息队列里
    • disconnect()停止观察器,直到再次调用observe()
    • takeRecords()将消息队列里未处理的变化通知全部删除并返回

详细参考MDN:MutationObserver

使用

<!DOCTYPE html>
<html>
<head>
	<title>Mutation Observer</title>
	<style type="text/css">
		.box {
			width: 100px;
			height: 100px;
			background-color: red;
		}

		.child{
			width: 50px;
			height: 50px;
			background-color: blue;
		}
	</style>
</head>
<body>
	<div class="box">
		<div class="child"></div>
	</div>

	<script type="text/javascript">
		const { log } = console;

		let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

		// box观察器的回调
		let boxCallback = (mutationList) => {
			log('box callback is called');
			for (let mutation of mutationList) {
				log(mutation);
				log(mutation.target);
				log('oldValue: ' + mutation.oldValue);
				log('');
			}
		};

		// 用于text观察器的回调
		let textCallback = (mutationList) => {
			log('text callback is called');
			for (let mutation of mutationList) {
				log(mutation);
				log(mutation.target);
				log('oldValue: ' + mutation.oldValue);
				log('');
			}
		};

		// 用于处理takeRecords()接收到的变化
		let takeRecordsCallback = (mutationList) => {
			log('take records callback is called');
			for (let mutation of mutationList) {
				log(mutation);
				log(mutation.target);
				log('oldValue: ' + mutation.oldValue);
				log('');
			}
		};

		let observer_box = new MutationObserver(boxCallback); // 此回调是异步的

		let observer_text = new MutationObserver(textCallback);

		let box = document.querySelector('.box');
		observer_box.observe(box, {
			childList: true,
			attributes: true,
			subtree: true,
			attributeFilter: ['style'],
			attributeOldValue: true // 开启后oldValue会记录变化,初始为null
		});

		box.style.width = '200px'; // MutationRecord {type: "attributes", oldValue: null, ...}
		box.style.width = '400px'; // MutationRecord {type: "attributes", oldValue: "width: 200px;", ...}

		box.appendChild(document.createElement('div')); // MutationRecord {type: "childList", oldValue: null, ...}

		let child = document.querySelector('.child');

		child.style.width = '100px'; // MutationRecord {type: "attributes", oldValue: null, ...}
		child.style.width = '200px'; // MutationRecord {type: "attributes", oldValue: "width: 100px;", ...}

		var mutations = observer_box.takeRecords(); // 将上面的MutationRecord都获取了,由于callback都是异步的,故上面的的callback未执行

		if (mutations) {
		  	takeRecordsCallback(mutations);
		}

		// log('observer_box will disconnect')
		// observer_box.disconnect();
        
		// 放入消息队列
		setTimeout(() => {
			log('observer_box will disconnect')
			observer_box.disconnect();
		}, 0);

		let textNode = document.createTextNode('1');
		child.appendChild(textNode); // MutationRecord {type: "childList", target: div.child, ...}

		// characterData需要用在text或comment元素才有效果
		observer_text.observe(textNode, {
			characterData: true,
			characterDataOldValue: true // oldValue初始为text的值
		});

		textNode.appendData('2'); // MutationRecord {type: "characterData", target: text, oldValue: "1"}
		textNode.appendData('3'); // MutationRecord {type: "characterData", target: text, oldValue: "12"}

	</script>
</body>
</html>

需要注意的是:

  • MutationObserver接受的callback执行是异步的,元素更变一般是同步代码,更变后callback会放入消息queue,等待当前一轮事件循环的结束才会执行
  • takeRecords()不是异步的,故可以取到当前再消息queue的元素变化
  • characterData参数只有在元素是textcomment类型时才有效果

参考