APICloud 外部网页实现 JS 脚本注入

2021年04月14日

因为项目需要在 APP 内部打开的网页中注入特定字符高亮的脚本,Google 半天也没有发现比较好的解答,所以写这篇博客记录一下踩的坑。

1 - 如何注入脚本

APICloud 官方提供了一个跨页面和窗口的脚本执行方法 execScript ,参数如下所示:

javascript 复制代码
api.execScript({
	frameName: _this.frameName, // 这里是需要注入的frame的名称,当然也可以使用 winName 注入 win 中
	script: js, // 这里是需要注入的 js 脚本字符串
});

但是这里有一个坑,如果直接注入进去会因为页面还未加载完毕导致一些脚本不会生效;我们可以使用官方提供的另一个方法来解决这个问题:setFrameClient

setFrameClient 方法是用来判断当前 frame 加载情况,并根据情况进行不同的操作,参数如下:

javascript 复制代码
api.setFrameClient(
	{
		frameName: _this.frameName,
	},
	(ret, err) => {
		if (ret.state === 2) {
			// state 用来判断当前加载状态,1 表示加载中,2 表示加载完成,其余状态可以查看官方文档
		}
	}
);
api.setFrameClient(
	{
		frameName: _this.frameName,
	},
	(ret, err) => {
		if (ret.state === 2) {
			// state 用来判断当前加载状态,1 表示加载中,2 表示加载完成,其余状态可以查看官方文档
		}
	}
);

接下来,我们只需要把写好的注入脚本函数放在 setFrameClient 的回调函数中执行即可;

2 - 页面高亮脚本

实现页面高亮可以使用正则表达式来实现效果,替换掉需要高亮的文本并修改页面的 innerHTML 属性:

javascript 复制代码
function keyLight(id, key, bgColor) {
	var oDiv = document.getElementById(id),
		sText = oDiv.innerHTML,
		bgColor = bgColor || 'orange',
		sKey =
			"<span style='background-color: " + bgColor + ";'>" + key + '</span>',
		num = -1,
		rStr = new RegExp(key, 'g'),
		rHtml = new RegExp('<.*?>', 'ig'), //匹配html元素
		aHtml = sText.match(rHtml); //存放html元素的数组
	sText = sText.replace(rHtml, '{~}'); //替换html标签
	sText = sText.replace(rStr, sKey); //替换key
	sText = sText.replace(/{~}/g, function () {
		//恢复html标签
		num++;
		return aHtml[num];
	});
	oDiv.innerHTML = sText;
}

对于没有动态加载功能或者点击查看更多功能的页面,使用这个函数并传入 body 的 DOM 对象就可以实现高亮了,但是由于替换了 body 的 html 代码,会出现很多奇怪的问题,所以最终我还是采用了比较耗时的遍历节点操作,只替换有指定文本的标签内容,避免破坏整体页面:

javascript 复制代码
function replaceNode(el, key) {
	let sKey = '<span style="color:#3f89e6;">' + key + '</span>';
	let rStr = new RegExp(key, 'g');
	if (!el.children && el.nodeType == 3) return;
	if (el.children.length == 0) {
		if (el.innerHTML && el.innerHTML.indexOf(key) !== -1) {
			el.innerHTML = el.innerHTML.replace(rStr, sKey);
		}
		return;
	}
	for (let i = 0; i < el.children.length; i++) {
		replaceNode(el.children[i], key);
	}
}

因为只是初步代码所以逻辑方面可能还会有点问题,以后会逐步优化一下,不过基本功能到此也算是实现了。

相关文章

Vite项目配置本地HTTPS

React Native 开发环境安装踩坑

《JavaScript 高级程序设计》第10-16章