例如,我想查找所有具有计算样式的元素position: fixed;
。如何在不对 CPU 造成太大负载的情况下做到这一点?
每次迭代getElementsByTagName('*')
然后做for循环是唯一的方法吗?
例如,我想查找所有具有计算样式的元素position: fixed;
。如何在不对 CPU 造成太大负载的情况下做到这一点?
每次迭代getElementsByTagName('*')
然后做for循环是唯一的方法吗?
您可以按照以下步骤操作,而不是选择所有 ( *
) 元素并使用getComputedStyle
+ getPropertyValue
:
遍历所有 CSS 规则(通过[1])并获取包含.document.styleSheets
position: fixed
style
选择其属性包含的所有元素position: fixed
。
用于document.querySelectorAll
选择与选择器匹配的所有元素。
window.getComputedStyle(elem, null).getPropertyValue('position')
equals是否fixed
与不在固定位置的过滤器元素(可能通过更具体的选择器或 覆盖!important
)。此时,您有一个包含所有position: fixed
元素的数组。
[1]
由于同源策略,外部样式表必须位于同一源。
//[style*=..] = attribute selector
var possibilities = ['[style*="position:fixed"],[style*="position: fixed"]'],
searchFor = /\bposition:\s*fixed;/,
cssProp = 'position',
cssValue = 'fixed',
styles = document.styleSheets,
i, j, l, rules, rule, elem, res = [];
for (i=0; i<styles.length; i++) {
rules = styles[i].cssRules;
l = rules.length;
for (j=0; j<l; j++) {
rule = rules[j];
if (searchFor.test(rule.cssText)) {
possibilities.push(rule.selectorText);
}
}
}
possibilities = possibilities.join(',');
possibilities = document.querySelectorAll(possibilities);
l = possibilities.length;
for (i=0; i<l; i++) {
elem = possibilities[i];
// Test whether the element is really position:fixed
if (window.getComputedStyle(elem, null).getPropertyValue(cssProp) === cssValue) {
res.push(elem);
}
}
res; //<-- = Array containing all position:fixed elements
我必须做类似的事情,我需要知道所有固定位置的元素,然后能够根据它们的位置(页面顶部或底部)切换它们的可见性。
下面的这个对象是我得到的解决方案,它使用 TreeWalker(或 IE 的旧节点遍历)通过计算样式来查找元素。对于现代浏览器来说,速度非常快。它可以改进,但是递归遍历方法经过了很好的测试并且具有可靠的性能。
window.fixedElementsTool = {
data: {
error: false,
method: typeof document.createTreeWalker == 'function' && typeof NodeFilter !== 'undefined' ? 'TreeWalker' : 'NodeTraversal',
viewport: { width: -1, height: -1 },
fixedElements: []
},
walker: null,
reject_tags: ['script','param'],
skip_tags: ['noscript', 'option'],
acceptNode: function(node){
var self = window.fixedElementsTool;
if (self.reject_tags.indexOf(node.tagName.toLowerCase()) > -1){
return self.data.method == 'TreeWalker' ? NodeFilter.FILTER_REJECT : false;
}
else if (self.skip_tags.indexOf(node.tagName.toLowerCase()) > -1){
return self.data.method == 'TreeWalker' ? NodeFilter.FILTER_SKIP : false;
}
else {
return self.data.method == 'TreeWalker' ? NodeFilter.FILTER_ACCEPT : true;
}
},
getStyle: function (el,styleProp){
try{
//access a computed style property - different from a css property
if (window.getComputedStyle){
return document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
}
else if (el.currentStyle){
return el.currentStyle[styleProp];
}
else {
return '';
}
}
catch(e){
return 'failed';
}
},
init: function(){
//initially add polyfills for JS functionality not in some browsers
this.addPolyfills();
//clear any elements from before
this.data.fixedElements = [];
try{
if (this.data.method == 'TreeWalker'){
this.walker = document.createTreeWalker( document.body, NodeFilter.SHOW_ELEMENT, this.acceptNode, false);
this.traverse();
}
else {
this.traverseIE(document.body);
}
this.data.viewport = this.getViewport();
}
catch(e){
//this will show us the browser's error message as an object
this.data.error = e;
}
finally{
if (this.data.error){
}
}
},
toggle: function(toggle, type){
if (this.data.fixedElements.length == 0){ this.init(); }
switch(type){
case 'all':
for (var i=0; i < this.data.fixedElements.length; i++){
var item = this.data.fixedElements[i];
item.node.style.display = toggle == 'hide' ? 'none' : item.originalDisplay;
};
break;
case 'top':
for (var i=0; i < this.data.fixedElements.length; i++){
var item = this.data.fixedElements[i];
//give it 5 pixels or so
if (item.rect.top <= 5){
item.node.style.display = toggle == 'hide' ? 'none' : item.originalDisplay;
}
};
break;
case 'bottom':
for (var i=0; i < this.data.fixedElements.length; i++){
//to get the actual offset FROM the bottom of the viewport
//subtract the rect.bottom value from the viewport height
var item = this.data.fixedElements[i],
offsetBottom = this.data.viewport.height - item.rect.bottom;
//give it 5 pixels or so
if (offsetBottom <= 5){
item.node.style.display = toggle == 'hide' ? 'none' : item.originalDisplay;
}
};
break;
}
},
traverse: function(){
//Main method for traversing and recording properties we want about each dom element
var position = this.getStyle(this.walker.currentNode,'position');
if (position == 'fixed'){
this.data.fixedElements.push({
node: this.walker.currentNode,
rect: this.walker.currentNode.getBoundingClientRect(),
originalDisplay: this.getStyle(this.walker.currentNode,'display') || ''
});
}
//if true then currentNode points to first child
if (this.walker.firstChild()){
this.traverse();
}
//if true then currentNode points next sibiling
if (this.walker.nextSibling()){
this.traverse();
}
else{
//set back to parent... this is our base case for recursive return
this.walker.parentNode();
return;
}
},
traverseIE: function(node){
//this is our base case
if (node == null){ return; }
//only store info for node of type ELEMENT which isn't in the rejected or skipped array of tags
if (node.nodeType === 1 && this.acceptNode(node)){
var position = this.getStyle(node,'position');
if (position == 'fixed'){
this.data.fixedElements.push({
node: node,
rect: node.getBoundingClientRect(),
originalDisplay: this.getStyle(node,'display') || ''
});
}
}
//if true then currentNode points to first child
if (node.firstChild && this.acceptNode(node)){
this.traverseIE(node.firstChild);
}
//if true then currentNode points next sibiling
if (node.nextSibling){
this.traverseIE(node.nextSibling);
}
},
getViewport: function(){
var viewport = { width: -1, height: -1 };
if (window.document.documentElement != undefined && window.document.documentElement != null){
viewport.width = window.document.documentElement.clientWidth;
viewport.height = window.document.documentElement.clientHeight;
}
else if (window.document.body != undefined && window.document.body != null){
viewport.width = window.document.body.clientWidth;
viewport.height = window.document.body.clientHeight;
}
return viewport;
},
addPolyfills: function(){
//ensure indexOf on arrays
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if ( this === undefined || this === null ) {
throw new TypeError( '"this" is null or not defined' );
}
// Hack to convert object.length to a UInt32
var length = this.length >>> 0;
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for (;fromIndex < length; fromIndex++) {
if (this[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
}
}
};
fixedElementsTool.init();
//fixedElementsTool.toggle('hide','all');
//fixedElementsTool.toggle('show','bottom');
//fixedElementsTool.toggle('show','top');
最好的办法是使用 DOM 遍历,因为在大多数浏览器中这是并行完成的,因此您将充分利用用户的核心。