Je suis en train d'écrire du JavaScript vanille pour créer un menu de navigation agréable. Je suis coincé sur l'ajout d'une classe active.
Je reçois des éléments par nom de classe, PAS par id. Le code ci-dessous fonctionne s'il est remplacé par id. Cependant, j'ai besoin qu'il s'applique à plusieurs éléments.
HTML
<img class="navButton" id="topArrow" src="images/arrows/top.png" />
<img class="navButton" id="rightArrow" src="images/arrows/right.png" />
JS
var button = document.getElementsByClassName("navButton");
button.onmouseover = function() {
button.setAttribute("class", "active");
button.setAttribute("src", "images/arrows/top_o.png");
}
Aucune réponse contenant jQuery s'il vous plaît.
document.getElementsByClassName
renvoie une liste de nœuds. Vous devrez donc parcourir la liste et lier l'événement à des éléments individuels. Comme ça...
var buttons = document.getElementsByClassName("navButton");
for(var i = 0; i < buttons.length; ++i){
buttons[i].onmouseover = function() {
this.setAttribute("class", "active");
this.setAttribute("src", "images/arrows/top_o.png");
}
}
Dans votre extrait, button
est une instance de NodeList
, à laquelle vous ne pouvez pas attacher directement un écouteur d'événement ni modifier les propriétés className
des éléments directement.
Votre meilleur pari est de déléguer l'événement:
document.body.addEventListener('mouseover',function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'img' && target.className.match(/\bnavButton\b/))
{
target.className += ' active';//set class
}
},false);
Bien entendu, j’imagine que la classe active
doit être supprimée une fois que l’événement mouseout
est déclenché. Vous pouvez envisager d’utiliser un deuxième délégant pour cela, mais vous pouvez associer également un gestionnaire d’événements a la classe active
:
document.body.addEventListener('mouseover',function(e)
{
e = e || window.event;
var oldSrc, target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'img' && target.className.match(/\bnavButton\b/))
{
target.className += ' active';//set class
oldSrc = target.getAttribute('src');
target.setAttribute('src', 'images/arrows/top_o.png');
target.onmouseout = function()
{
target.onmouseout = null;//remove this event handler, we don't need it anymore
target.className = target.className.replace(/\bactive\b/,'').trim();
target.setAttribute('src', oldSrc);
};
}
},false);
Il y a quelques améliorations à apporter avec ce code, mais je ne vais pas m'amuser ici ;-).
Voici une méthode adaptée de Jquery 2.1.1 qui utilise un élément dom au lieu d’un objet jquery (jquery n’est donc pas nécessaire). Comprend les vérifications de type et les expressions rationnelles:
function addClass(element, value) {
// Regex terms
var rclass = /[\t\r\n\f]/g,
rnotwhite = (/\S+/g);
var classes,
cur,
curClass,
finalValue,
proceed = typeof value === "string" && value;
if (!proceed) return element;
classes = (value || "").match(rnotwhite) || [];
cur = element.nodeType === 1
&& (element.className
? (" " + element.className + " ").replace(rclass, " ")
: " "
);
if (!cur) return element;
var j = 0;
while ((curClass = classes[j++])) {
if (cur.indexOf(" " + curClass + " ") < 0) {
cur += curClass + " ";
}
}
// only assign if different to avoid unneeded rendering.
finalValue = cur.trim();
if (element.className !== finalValue) {
element.className = finalValue;
}
return element;
};
getElementsByClassName()
renvoie HTMLCollection afin que vous puissiez essayer ceci
var button = document.getElementsByClassName("navButton")[0];
Modifier
var buttons = document.getElementsByClassName("navButton");
for(i=0;buttons.length;i++){
buttons[i].onmouseover = function(){
this.className += ' active' //add class
this.setAttribute("src", "images/arrows/top_o.png");
}
}
J'aime utiliser une fonction "foreach" personnalisée pour ce genre de choses:
function Each( objs, func )
{
if ( objs.length ) for ( var i = 0, ol = objs.length, v = objs[ 0 ]; i < ol && func( v, i ) !== false; v = objs[ ++i ] );
else for ( var p in objs ) if ( func( objs[ p ], p ) === false ) break;
}
(Je ne me souviens plus où j'ai trouvé la fonction ci-dessus, mais cela a été très utile.)
Ensuite, après avoir récupéré vos objets (à elements
dans cet exemple), faites simplement
Each( elements, function( element )
{
element.addEventListener( "mouseover", function()
{
element.classList.add( "active" );
//element.setAttribute( "class", "active" );
element.setAttribute( "src", "newsource" );
});
// Remove class and new src after "mouseover" ends, if you wish.
element.addEventListener( "mouseout", function()
{
element.classList.remove( "active" );
element.setAttribute( "src", "originalsource" );
});
});
classList
est un moyen simple de gérer les classes d'éléments. Juste besoin d'une cale pour quelques navigateurs. Si vous devez utiliser setAttribute
, vous devez vous rappeler que tout ce qui y est associé will écrasera les valeurs précédentes.
EDIT: J'ai oublié de mentionner que vous devez utiliser attachEvent
au lieu de addEventListener
sur certaines IE versions. Testez avec if ( document.addEventListener ) {...}
.
La boucle forEach est intégrée dans un tableau dans ECMAScript 5th Edition.
var buttons = document.getElementsByClassName("navButton");
Array.prototype.forEach.call(buttons,function(button) {
button.setAttribute("class", "active");
button.setAttribute("src", "images/arrows/top_o.png");
});
Ajoutez simplement un nom de classe au début de la fonction et les 2ème et 3ème arguments sont optionnels et la magie est faite pour vous!
function getElementsByClass(searchClass, node, tag) {
var classElements = new Array();
if (node == null)
node = document;
if (tag == null)
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp('(^|\\\\s)' + searchClass + '(\\\\s|$)');
for (i = 0, j = 0; i < elsLen; i++) {
if (pattern.test(els[i].className)) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}