Can I select multiple tags using getElementsByTagName?

不打扰是莪最后的温柔 提交于 2019-12-18 11:42:02

问题


I'm using a javascript snippet in order for visitors to my site to increase the font size on all paragraphs using the following 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"
    } 
} 

How can I also include "li" to this code so that "p" and "li" are the selected elements that are affected?

I would also like to avoid adding a class or an id to my "li" or "ul". Is there a way to select two tags at once?


回答1:


No, you can't select multiple tags with a single call to getElementsByTagName. You can either do two queries using getElementsByTagName or use querySelectorAll.

JSFiddle

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



回答2:


A year late, but if you intend on using the desired functionality multiple times in your project, and you don't have access to querySelector(), it might be worth extending the Node object with a simple function:

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;
};

Working demo on JSFiddle.

All it does is iterating over an array of tag names, then getting the corresponding elements using getElementsByTagName() for each iteration.

This can then of course be used on any element the same exact way you use similar functions - for example, getElementById() - on any Node object, you are not limited to document.




回答3:


Q

Can I select multiple tags using getElementsByTagName?

A

Yes, but you will have to use getElementsByTagName multiple times.

Although your example only specifies Document.getElementsByTagName() I have assumed you would like this to work with element.getElementsByTagName() as well.

getElementsByTagName returns a HTMLCollection object so the ideal outcome would be a method which returns a HTMLCollection object of elements for all tag names provided.

Things to note about HTMLCollection's

  • They cannot be modified.
  • They are a live list of DOM nodes
  • There are only three ways to create one yourself directly getElementsByTagName, getElementsByClassName and getElementsByTagNameNS
  • you can create an object which may have properties which are of type HTMLCollection e.g nodeList.children

As HTMLCollection's cannot be modified the best we can do is either return an object which resembled a HTMLCollection's as much as possible, see Create a HTMLCollection or to create an nodeList and return the children property.

Firstly we need to collect all the matching elements for our HTMLCollection

The simplest way would be to use the querySelectorAll function which returns a nodeList.

var nodeList = document.querySelectorAll(selector);

An alternative would be to use the getElementsByTagName method for each tag, convert the returned HTMLCollection object to an array so they can be merged together.

Like so .

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]))); 
}

The nodeList can also be converted to an array using the same method.

HTMLCollectionArray = Array.prototype.slice.call(nodeList);

We can now either return all the elements as an array or try to return a HTMLCollection.

If we were to return a HTMLCollection it would have to be either move or copy the elements to a single parentNode so we can access parentNode.children.

I found using document.createDocumentFragment works best.

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

Although this would return the correct type(HTMLCollection) it does not return the actual state of the elements when the method was called. The DOM has been modified to achieve this. Not a good idea.

So this leaves us with making a Fake 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;
}

Usage

var HTMLCollection = new MyHTMLCollection(elementsArray);

Now to piece it all together.

Ive also implemented a 'getElementsByClassNames' method and well as 'getElementsByTagNames' which both use the same core method 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');
}

We ONLY want the Document and Element interfaces to inherit our new methods because they call prototype methods which do not exist in all Node interfaces. e.g. getElementsByClassName,querySelectorAll, etc.

If you want to minify your code then you could use Node.prototype instead of stating Element.prototype. and Document.prototype.

Node.prototype.getElementsByTagNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByTagName');
}
Node.prototype.getElementsByClassNames = function(selector){
    return this.getElementsBySelector(selector, 'getElementsByClassName');
}

Just make sure you don't try to use it on any node which isn't Document or Element.

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;
    */
}

Usage

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'); 


来源:https://stackoverflow.com/questions/21193437/can-i-select-multiple-tags-using-getelementsbytagname

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!