web-dev-qa-db-fra.com

Puis-je sélectionner plusieurs balises à l'aide de getElementsByTagName?

J'utilise un extrait de code javascript pour que les visiteurs de mon site augmentent la taille de la police sur tous les paragraphes en utilisant le javascript suivant:

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

Comment puis-je également inclure "li" dans ce code afin que "p" et "li" soient les éléments sélectionnés qui sont affectés?

Je voudrais également éviter d'ajouter une classe ou un identifiant à mon "li" ou "ul". Existe-t-il un moyen de sélectionner deux balises à la fois?

47
biddybump

Non, vous ne pouvez pas sélectionner plusieurs balises en un seul appel à getElementsByTagName. Vous pouvez effectuer deux requêtes à l'aide de getElementsByTagName ou utiliser querySelectorAll.

JSFiddle

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

Q

Puis-je sélectionner plusieurs balises à l'aide de getElementsByTagName?

UNE

Oui, mais vous devrez utiliser getElementsByTagName plusieurs fois.

Bien que votre exemple spécifie uniquement Document.getElementsByTagName () J'ai supposé que vous souhaitiez que cela fonctionne également avec element.getElementsByTagName ().

getElementsByTagName renvoie un objet HTMLCollection donc le résultat idéal serait une méthode qui renvoie un objet HTMLCollection des éléments pour tous les noms de balise fournis.

Choses à noter sur HTMLCollection's

  • Ils ne peuvent pas être modifiés.
  • Ce sont une live liste de nœuds DOM
  • Il n'y a que trois façons d'en créer une vous-même directement getElementsByTagName, getElementsByClassName et getElementsByTagNameNS
  • vous pouvez créer un objet qui peut avoir des propriétés de type HTMLCollection, par exemple nodeList.children

Comme HTMLCollection's ne peut pas être modifié, le mieux que nous puissions faire est de renvoyer autant que possible un objet qui ressemblait à HTMLCollection , voir Créer une HTMLCollection ou pour créer un nodeList et renvoyer la propriété children.

Premièrement, nous devons collecter tous les éléments correspondants pour notre HTMLCollection

La manière la plus simple serait d'utiliser la fonction querySelectorAll qui renvoie un nodeList.

var nodeList = document.querySelectorAll(selector);

Une alternative serait d'utiliser la méthode getElementsByTagName pour chaque balise, de convertir l'objet HTMLCollection retourné en un tableau afin qu'ils puissent être fusionnés ensemble.

Ainsi .

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

La nodeList peut également être convertie en un tableau en utilisant la même méthode.

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

Nous pouvons maintenant soit renvoyer tous les éléments sous forme de tableau, soit essayer de renvoyer une HTMLCollection.

Si nous devions renvoyer une HTMLCollection, il faudrait soit déplacer, soit copier les éléments dans un seul parentNode afin que nous puissions accéder à parentNode.children.

J'ai trouvé en utilisant document.createDocumentFragment fonctionne mieux.

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

Bien que cela renvoie le type correct (HTMLCollection), il ne renvoie pas l'état réel des éléments lors de l'appel de la méthode. Le DOM a été modifié pour y parvenir. Pas une bonne idée.

Donc, cela nous laisse avec faire un faux 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);

Maintenant, pour tout assembler.

Ive a également implémenté une méthode "getElementsByClassNames" ainsi que "getElementsByTagNames" qui utilisent toutes les deux la même méthode principale 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');
}

Nous voulons UNIQUEMENT que les interfaces Document et Element héritent de nos nouvelles méthodes car elles appellent des méthodes prototypes qui n'existent pas dans toutes les interfaces Node . par exemple. getElementsByClassName, querySelectorAll, etc.

Si vous souhaitez réduire votre code, vous pouvez utiliser Node.prototype au lieu d'indiquer Element.prototype. et Document.prototype.

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

Assurez-vous simplement de ne pas l'utiliser sur un nœud qui n'est pas Document ou 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'); 
2
TarranJones

Un an de retard, mais si vous avez l'intention d'utiliser plusieurs fois la fonctionnalité souhaitée dans votre projet et que vous n'avez pas accès à querySelector () , il peut être utile d'étendre le Node objet avec une fonction simple :

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

Démo de travail sur JSFiddle.

Tout ce qu'il fait est d'itérer sur un tableau de noms de balises, puis d'obtenir les éléments correspondants en utilisant getElementsByTagName() pour chaque itération.

Ceci peut alors bien sûr être utilisé sur l'élément any de la même manière que vous utilisez des fonctions similaires - par exemple, getElementById() - sur n'importe quel objet Node, vous ne sont pas limités à document.

1
John Weisz