我正在编写一个在页面上执行某些功能的书签。作为此功能的一部分,我需要使用getElementsByClassName
。但是,在测试过程中,我发现有几个网站已将getElementsByClassName
重新定义为自定义方法。据推测,这是为了在所有浏览器中支持getElementsByClassName
。
自定义getElementsByClassName
的实现有点草率,我的几个用例都失败了。有什么方法可以得到getElementsByClassName
的原始定义吗?
在chrome javascript控制台中:getElementsByClassName
指向本机功能。有没有办法访问这个原生函数,现在getElementsByClassName
已被重新定义?
答案 0 :(得分:3)
有一个解决方案,但它并不健全。请参阅底部的免责声明。
如果您打开一个新窗口,您将可以访问其未修改的方法。我可以想到两种打开新窗口的方法:使用window.open
和iframe。 iframe不那么突兀,因为它不会通过打开新的浏览器窗口或触发弹出窗口阻止程序来分散用户的注意力。
// Runs fn with a clean window reference
function getCleanReferences(fn) {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
var newWindow = iframe.contentWindow;
iframe.parentNode.removeChild(iframe);
return fn(newWindow);
}
有些浏览器不喜欢删除iframe。如果您希望在Safari或Firefox中使用此功能,请删除removeChild行。接下来,您将需要从新窗口获取getElementsByClassName方法。
var nativeGetElementsByClassName = getCleanReferences(function(newWindow) {
var getElementsByClassName = newWindow.document.getElementsByClassName;
return function(className) {
return getElementsByClassName.call(window.document, className);
};
});
这几乎是跨浏览器,除了IE在怪癖模式下或版本9(你在书签中几乎无法控制)之前不喜欢getElementsByClassName。这是一个jsfiddle:http://jsfiddle.net/theazureshadow/vdqYG/
如果您对我导入其他方法的实验感到好奇,请看看这个jsfiddle:http://jsfiddle.net/theazureshadow/ccFG3/
免责声明:在当前环境中使用其他窗口中的方法是危险的,未定义的区域。如果您选择使用这种脆弱的方法,请确保测试跨浏览器兼容性。我的测试显示了不同浏览器如何处理这种方法的主要差异。您不应该在生产站点上使用它,但它可能适用于个人书签。仅包含您想要使用的任何方法的版本可能更安全。
答案 1 :(得分:2)
我认为这是不可能的。这就是为什么monkeypatching是坏的。永远不应该重新定义主机对象。
答案 2 :(得分:1)
我猜有些网站使用原型。 DOM的document.getElementsByClassName()
返回NodeList
,而Prototype的document.getElementsByClassName()
返回Array
,因为JS-Scripts无法创建NodeList
。
在Firefox中你可以使用
var node = document;
Components.lookupMethod(node, 'getElementsByClassName').call(node, /* className */);
获取原始方法。也许谷歌Chrome实现类似的东西,否则你将失去运气。我找不到任何东西(2分钟谷歌搜索)。
在这种情况下,您可以使用以下内容:
function getElementsByClassName(node, className) {
var rv = new Array();
var nodeList = node.getElementsByTagName('*');
className = className.replace(/([\.\\\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-])/, '\\$1');
var regex = new RegExp('(?:^|[\\n\\r ])' + className + '(?:[\\n\\r ]|$)');
for(var length = nodeList.length, i = 0; i < length; ++i) {
if(regex.test(nodeList[ i ].className)) {
rv.push(nodeList[ i ]);
}
}
return rv;
}
如果Google Chrome实施node.classList
,您可以使用以下不使用令人毛骨悚然的正则表达式的功能:
function getElementsByClassName(node, className) {
var rv = new Array();
var nodeList = node.getElementsByTagName('*');
for(var length = nodeList.length, i = 0; i < length; ++i) {
if(nodeList[ i ].classList.contains(className)) {
rv.push(nodeList[ i ]);
}
}
return rv;
}
此函数迭代给定节点内的所有元素。它返回一个类似Prototype的数组并且有相同的缺点:数组不像NodeList
那样“活动”。
答案 3 :(得分:0)
这取决于自定义方法的定义方式。您可以像Element.prototype.getElementsByClassName
一样访问该方法,但如果该页面覆盖了这个原型方法,我认为没有办法将其恢复。