我能使用getElementsByTagName选择多个标签吗?

72

我正在使用一个 JavaScript 代码片段,让访问我的网站的用户可以通过以下 JavaScript 来增大所有段落的字体大小:

function increaseFontSize() {  

    var paragraphs = document.getElementsByTagName('p'); 

    for(i=0;i<paragraphs.length;i++) {   

        if(paragraphs[i].style.fontSize) { 
            var s = parseInt(paragraphs[i].style.fontSize.replace("px",""));
        } else {   
            var s = 14;
        }

        if(s != max) {  
            s += 1; 
        } 
        paragraphs[i].style.fontSize = s+"px"
    } 
} 

如何将"li"也包含在这段代码中,以便选择的元素为"p"和"li"?

我还想避免给我的"li"或"ul"添加类或ID。有没有办法同时选择两个标签?


1
我不知道为什么人们试图避免添加类和ID,这些都是有帮助的。你应该避免添加太多类,但一个是可以的。 - user2203117
5个回答

121

不,你不能使用单个getElementsByTagName调用选择多个标签。你可以使用getElementsByTagName进行两次查询,或者使用querySelectorAll

JSFiddle

var elems = document.querySelectorAll('p,li')

12
document.querySelectorAll('input,textarea,select') - 对于表单非常实用 - 谢谢 - Johnny Darvall
7
我建议您将“不行,你不能这样做”改为“有另一种方法来完成这件事”。 - Ali
1
getElementsByTagName支持正则表达式。所以,是的,你可以! - barwnikk
2
@barwnikk 这里是w3c和whatwg规范,根据它们的规定,您不能使用正则表达式 http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1938918D https://dom.spec.whatwg.org/#dom-element-getelementsbytagname - Daniel Imms
3
问题明确询问是否可以在单个函数调用中完成。很显然,你可以用两个调用来完成它。 - Daniel Imms
显示剩余2条评论

3
一年晚了,但如果你打算在项目中多次使用所需的功能,并且没有访问querySelector()的权限,则值得使用一个简单的函数扩展Node对象:

JavaScript

/**
 * @param {Array} tags - The array of tagNames to search for.
 * @return {Array}     - The elements with matching tagNames.
 */
Node.prototype.getElementsByTagNames = function (tags) {
    var elements = [];

    for (var i = 0, n = tags.length; i < n; i++) {
        // Concatenate the array created from a HTMLCollection object
        elements = elements.concat(Array.prototype.slice.call(this.getElementsByTagName(tags[i])));
    }

    return elements;
};

JSFiddle上的工作演示。

它所做的就是迭代标签名称数组,然后使用getElementsByTagName()获取每次迭代的相应元素。

当然,您可以在任何Node对象上以与类似函数 - 例如getElementById() - 相同的方式使用它,您不受限于document


我对此进行了轻微更改,其中tags是逗号分隔的字符串(即“ input,textarea”),并在内部运行tags.split(“,”)。这样,它就可以像getElementsByTagName一样运行。这是一个很好的答案。 - Adam

2

Q

我可以使用getElementsByTagName选择多个标签吗?

A

是的,但您将不得不多次使用getElementsByTagName。
虽然您的示例仅指定了Document.getElementsByTagName(), 但我假设您希望这也适用于element.getElementsByTagName()。
getElementsByTagName返回一个HTMLCollection对象,因此理想的结果是一个方法,该方法返回所有提供的标记名称的元素的HTMLCollection对象。
关于HTMLCollection's需要注意的事项。
  • 它们无法被修改。
  • 它们是DOM节点的实时列表
  • 只有三种方法可以直接创建一个,即getElementsByTagNamegetElementsByClassNamegetElementsByTagNameNS
  • 您可以创建一个对象,该对象可能具有类型为HTMLCollection的属性,例如nodeList.children

由于HTMLCollection无法被修改,我们能做的最好的办法就是返回一个尽可能类似于HTMLCollection的对象,参见创建HTMLCollection,或者创建一个nodeList并返回children属性。

首先,我们需要收集所有匹配元素以获取我们的HTMLCollection

最简单的方法是使用querySelectorAll函数,它返回一个nodeList
var nodeList = document.querySelectorAll(selector);

另一种方法是对每个标签使用 getElementsByTagName 方法,将返回的 HTMLCollection 对象转换为数组,然后将它们合并在一起。

像这样。

var HTMLCollectionArray = [];
var names = selector.split(",");
for (var i = 0, n = names.length; i < n; i++){
    HTMLCollectionArray = HTMLCollectionArray.concat(Array.prototype.slice.call(document.getElementsByTagName(names[i]))); 
}

节点列表(nodeList)也可以使用相同的方法转换为数组。
HTMLCollectionArray = Array.prototype.slice.call(nodeList);

我们现在可以将所有元素作为数组返回,或者尝试返回HTMLCollection。
如果我们要返回HTMLCollection,就必须将元素移动或复制到单个parentNode,以便我们可以访问parentNode.children。
我发现使用document.createDocumentFragment效果最好。
var createDocumentFragment = document.createDocumentFragment();
for (var i = 0; i < HTMLCollectionArray.length; i++) {
    createDocumentFragment.appendChild(HTMLCollectionArray[i]);
};
HTMLCollection = createDocumentFragment.children; 
return HTMLCollection;

虽然这样可以返回正确的类型(HTMLCollection),但它并不返回方法被调用时元素的实际状态。DOM 已经被修改以实现这一点。这不是一个好主意。
因此,我们只能制作一个假的 HTMLCollection。
window.MyNodeList = function(elements) {

    for ( var i = 0; i < elements.length; i += 1 ) {
        this[i] = elements[i];
    }
    Object.defineProperty( this, 'length', {
        get: function () {
            return elements.length;
        }
    });
    Object.freeze( this );
};

window.MyNodeList.prototype.item  function ( i ) {
    return this[i] != null ? this[i] : null;
}

window.MyHTMLCollection =  function(elements) {
  MyNodeList.call(this, elements);
}

MyHTMLCollection.prototype = Object.create(MyNodeList.prototype);

MyHTMLCollection.prototype.constructor = MyHTMLCollection;

window.MyHTMLCollection.prototype.namedItem =  function ( name ) {
    for ( var i = 0; i < this.length; i += 1 ) {
        if ( this[i].id === name || this[i].name === name ) {
            return this[i];
        }
    }
    return null;
}

使用方法。
var HTMLCollection = new MyHTMLCollection(elementsArray);

现在把所有东西拼起来。
我还实现了一个“getElementsByClassNames”方法和一个“getElementsByTagNames”方法,它们都使用相同的核心方法getElementsBySelector
Element.prototype.getElementsByTagNames = Document.prototype.getElementsByTagNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByTagName');
}
Element.prototype.getElementsByClassNames = Document.prototype.getElementsByClassNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByClassName');
}

我们只希望文档元素接口继承我们的新方法,因为它们调用了不是所有节点接口都存在的原型方法。例如:getElementsByClassNamequerySelectorAll等。
如果您想要压缩您的代码,那么您可以使用Node.prototype而不是声明Element.prototype.Document.prototype.
Node.prototype.getElementsByTagNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByTagName');
}
Node.prototype.getElementsByClassNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByClassName');
}

请确保不要在任何不是文档元素的节点上使用它。
Element.prototype.getElementsBySelector = Document.prototype.getElementsBySelector = function (selector, HTMLCollectionType) {

    var HTMLCollectionArray = [];

    if(typeof this.querySelectorAll !== 'undefined'){

        var nodeList = this.querySelectorAll(selector);
        HTMLCollectionArray = Array.prototype.slice.call(nodeList);

    } else {

        if(typeof HTMLCollectionType !=='undefined' && typeof this[HTMLCollectionType] !== 'undefined'){

            var names = selector.split(",");
            for (var i = 0, n = names.length; i < n; i++){
                HTMLCollectionArray = HTMLCollectionArray.concat(Array.prototype.slice.call(this[HTMLCollectionType](names[i]))); 
            }
        }
    }

    return new MyHTMLCollection(HTMLCollectionArray);

    /* 
    var createDocumentFragment = document.createDocumentFragment();
    for (var i = 0; i < HTMLCollectionArray.length; i++) {
        createDocumentFragment.appendChild(HTMLCollectionArray[i]);
    };
    HTMLCollection = createDocumentFragment.children;
    return HTMLCollection;
    */
}

使用方法
var element = document.getElementById('id');
element.getElementsbyClassNames('class1,class2,class2'); 
element.getElementsbyTagNames('li,div,p'); 

document.getElementsbyClassNames('class1,class2,class2'); 
document.getElementsbyTagNames('li,div,p'); 

0

您可以使用getElementsByTagName选择多个标签,以便拥有一个可迭代的数组,其中多个标签(例如P和LI)的结果可以一起处理。
无法通过一次调用getElementsByTagName()来完成此操作,但可以链接两个getElementsByTagName()并避免更复杂的代码或使用querySelectorAll()。

这是您的完整示例。

function increaseFontSize() {  

    Array.from(document.getElementsByTagName('p'))
        .concat(Array.from(document.getElementsByTagName('li'))).forEach(el => {


            if(el.style.fontSize) { 
                var s = parseInt(el.style.fontSize.replace("px",""));
            } else {   
                var s = 14;
            }

            if(s != max) {  
                s += 1; 
            } 
            el.style.fontSize = s+"px"
    });
} 

解释:
Array.from()
这是官方的方法,可以从集合中创建真正的数组。
一些浏览器可能支持在HTMLCollection上使用forEach(),但这不在规范中。
即使如此,它们可能也不支持concat()方法。
使用from()将创建集合的浅拷贝。
这对于HTMLCollection来说是一个优点,因为在迭代期间可能会修改项目, 而对数组内部元素的修改仍将是原始对象上的修改。

现在我们有了两个标准数组,可以使用concat()将它们连接起来,并使用forEach()迭代连接结果。


-2

这只在Chrome中有效,并不是解决方案,但被发现是JavaScript行为的一个显著特点:

document.getElementsByTagName('p' || 'li') //-> selects both P and LI elements.

它甚至可以与 HTML 集合一起使用,在那里您可能只想仅选择同时包含多种标记的元素。可以像这样完成:

var table = document.getElementByID('myTable');
var rows = table.getElementsByTagName('tr');

rows[1].getElementsByTagName('input' && 'select') //-> select only when both input and select are present

1
我怀疑它是否有效(对我来说肯定无效)。'p' || 'li' 总是返回 'p',因为它被强制转换为 true,而 'input' && 'select' 由于同样的原因总是返回 'select' - shaedrich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接