因为项目需要在 APP 内部打开的网页中注入特定字符高亮的脚本,Google 半天也没有发现比较好的解答,所以写这篇博客记录一下踩的坑。
APICloud 官方提供了一个跨页面和窗口的脚本执行方法 execScript ,参数如下所示:
api.execScript({
frameName: _this.frameName, // 这里是需要注入的frame的名称,当然也可以使用 winName 注入 win 中
script: js, // 这里是需要注入的 js 脚本字符串
});
但是这里有一个坑,如果直接注入进去会因为页面还未加载完毕导致一些脚本不会生效;我们可以使用官方提供的另一个方法来解决这个问题:setFrameClient
setFrameClient 方法是用来判断当前 frame 加载情况,并根据情况进行不同的操作,参数如下:
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 的回调函数中执行即可;
实现页面高亮可以使用正则表达式来实现效果,替换掉需要高亮的文本并修改页面的 innerHTML 属性:
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 代码,会出现很多奇怪的问题,所以最终我还是采用了比较耗时的遍历节点操作,只替换有指定文本的标签内容,避免破坏整体页面:
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);
}
}
因为只是初步代码所以逻辑方面可能还会有点问题,以后会逐步优化一下,不过基本功能到此也算是实现了。