Can I select multiple tags using getElementsByTagName?

后端 未结 4 646
天命终不由人
天命终不由人 2020-12-28 11:28

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 increas         


        
4条回答
  •  太阳男子
    2020-12-28 12:09

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

提交回复
热议问题