使用 jQuery 和 contains() 进行全词匹配
Whole-word matching with jQuery and contains()
我正在编写一个 Greasemonkey 脚本来有选择地隐藏包含讨厌内容的元素(如果您愿意,可以使用个人网络消毒剂)。
这是我目前得到的:
//custom contains function which is case-insensitive
$.extend($.expr[":"], {
"containsNC": function(elem, i, match, array) {
return (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
}
});
//build array of words to filter
var nope = "long list of horrible words".toLowerCase().split(' ');
//start with an empty jQuery object
var nopeEles = $();
//add elements to filter to it
for (var i = 0; i < nope.length; i++) {
nopeEles = nopeEles.add( $("a:containsNC('" + nope[i] + "')") );
nopeEles = nopeEles.add( $("p:containsNC('" + nope[i] + "')") );
}
//hide all applicable elements
nopeEles.css("background-color", "white");
nopeEles.css("color", "white");
还算可以,但是做的是部分词匹配,导致短词不能用。我想过滤包含 "die" 和 "gun" 等词的元素,而不过滤包含 "candied" 或 "gung-ho".
等词的元素
需要说明的是,我追求的是整个单词,而不是精确的文本。我希望列表中的 "gun" 不仅匹配 "gun",还匹配 "he fired a gun" 和 "a gun was fired"。而不是 "gunney sergeant".
我在这个主题上看到的所有其他答案都推荐 jQuery 的 filter()。我想我还不够了解。我尝试在循环中使用这一行,但什么也没有:
nopeEles = nopeEles.add( $("a").filter(function() { return $(this).text() === nope[i]; }) );
我想看的另一个角度是摆弄 containsNC 所以它寻找这个词,但两边都有空格或字符串结尾。不过,我真的不明白 containsNC 是如何工作的。
任何指点将不胜感激!
containsNC
只是 this p:containsCI()
jQuery extension 的次等版本。
("NC" == "no case" ≈≈ "CI" == "Case insensitive".)
改用链接的 jQuery 扩展名,然后您可以使用正则表达式来匹配整个单词,例如:
nopeEles = nopeEles.add( $("a:containsCI('\b" + nope[i] + "\b')") );
但是,该问题代码效率很低,您会发现它会减慢页面速度,因为它扫描整个页面 2N 次(其中 N 是术语数)乘以 J 次子字符串扫描(其中 J 是<a>
和 <p>
个节点数)。
一种更高效的方法是通过合并正则表达式只扫描每个节点一次。请参阅此演示:
jQuery.extend (
jQuery.expr[':'].containsCI = function (a, i, m) {
var sText = (a.textContent || a.innerText || "");
var zRegExp = new RegExp (m[3], 'i');
return zRegExp.test (sText);
}
);
//-- Build array of terms to filter:
var badTerms = ['die', 'guns?', 'agitators?'];
//-- Build ONE regex string for speed and efficiency:
var cnsrRegEx = `\b(${badTerms.join ("|")})\b`; // \b is word-break regex.
var nopeEles = $("a, p").filter (":containsCI('" + cnsrRegEx + "')");
//-- Hide all applicable elements:
nopeEles.css ( {
"background-color": "white",
"color": "white"
} );
a, p {border: 1px solid lightgray; padding: 0.3ex 1ex;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p>All good</p>
<p>All bad agitators</p>
<div>Some bad: <a>die</a> <a>gun</a> <a>candied</a> <a>gung-ho</a> <a>guns</a>
<a>he fired a gun</a> <a>gunney sergeant</a>
</div>
注:
- 像
guns?
这样的正则表达式允许匹配 "gun" 和 "guns"。
- 由于我们正在构建将转换为正则表达式的 字符串,因此必须转义
\
个字符。那就是使用 "\b"
在正则表达式中得到 \b
。
我正在编写一个 Greasemonkey 脚本来有选择地隐藏包含讨厌内容的元素(如果您愿意,可以使用个人网络消毒剂)。
这是我目前得到的:
//custom contains function which is case-insensitive
$.extend($.expr[":"], {
"containsNC": function(elem, i, match, array) {
return (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
}
});
//build array of words to filter
var nope = "long list of horrible words".toLowerCase().split(' ');
//start with an empty jQuery object
var nopeEles = $();
//add elements to filter to it
for (var i = 0; i < nope.length; i++) {
nopeEles = nopeEles.add( $("a:containsNC('" + nope[i] + "')") );
nopeEles = nopeEles.add( $("p:containsNC('" + nope[i] + "')") );
}
//hide all applicable elements
nopeEles.css("background-color", "white");
nopeEles.css("color", "white");
还算可以,但是做的是部分词匹配,导致短词不能用。我想过滤包含 "die" 和 "gun" 等词的元素,而不过滤包含 "candied" 或 "gung-ho".
等词的元素需要说明的是,我追求的是整个单词,而不是精确的文本。我希望列表中的 "gun" 不仅匹配 "gun",还匹配 "he fired a gun" 和 "a gun was fired"。而不是 "gunney sergeant".
我在这个主题上看到的所有其他答案都推荐 jQuery 的 filter()。我想我还不够了解。我尝试在循环中使用这一行,但什么也没有:
nopeEles = nopeEles.add( $("a").filter(function() { return $(this).text() === nope[i]; }) );
我想看的另一个角度是摆弄 containsNC 所以它寻找这个词,但两边都有空格或字符串结尾。不过,我真的不明白 containsNC 是如何工作的。
任何指点将不胜感激!
containsNC
只是 this p:containsCI()
jQuery extension 的次等版本。
("NC" == "no case" ≈≈ "CI" == "Case insensitive".)
改用链接的 jQuery 扩展名,然后您可以使用正则表达式来匹配整个单词,例如:
nopeEles = nopeEles.add( $("a:containsCI('\b" + nope[i] + "\b')") );
但是,该问题代码效率很低,您会发现它会减慢页面速度,因为它扫描整个页面 2N 次(其中 N 是术语数)乘以 J 次子字符串扫描(其中 J 是<a>
和 <p>
个节点数)。
一种更高效的方法是通过合并正则表达式只扫描每个节点一次。请参阅此演示:
jQuery.extend (
jQuery.expr[':'].containsCI = function (a, i, m) {
var sText = (a.textContent || a.innerText || "");
var zRegExp = new RegExp (m[3], 'i');
return zRegExp.test (sText);
}
);
//-- Build array of terms to filter:
var badTerms = ['die', 'guns?', 'agitators?'];
//-- Build ONE regex string for speed and efficiency:
var cnsrRegEx = `\b(${badTerms.join ("|")})\b`; // \b is word-break regex.
var nopeEles = $("a, p").filter (":containsCI('" + cnsrRegEx + "')");
//-- Hide all applicable elements:
nopeEles.css ( {
"background-color": "white",
"color": "white"
} );
a, p {border: 1px solid lightgray; padding: 0.3ex 1ex;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p>All good</p>
<p>All bad agitators</p>
<div>Some bad: <a>die</a> <a>gun</a> <a>candied</a> <a>gung-ho</a> <a>guns</a>
<a>he fired a gun</a> <a>gunney sergeant</a>
</div>
注:
- 像
guns?
这样的正则表达式允许匹配 "gun" 和 "guns"。 - 由于我们正在构建将转换为正则表达式的 字符串,因此必须转义
\
个字符。那就是使用"\b"
在正则表达式中得到\b
。