Добавить класс с JavaScript
Я пишу несколько ванильных JavaScript для создания приятного меню навигации. Я застрял в добавлении активного класса.
Я получаю элементы по имени класса NOT по id. Приведенный ниже код работает, если он заменен идентификатором, однако мне нужно, чтобы он применялся к нескольким элементам.
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");
}
Нет ответов, содержащих jQuery.
Ответы
Ответ 1
document.getElementsByClassName
возвращает список node. Таким образом, вам придется перебирать список и привязывать событие к отдельным элементам. Как это...
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");
}
}
Ответ 2
В вашем фрагменте button
есть экземпляр NodeList
, к которому вы не можете напрямую присоединить прослушиватель событий, а также не можете напрямую изменять свойства элементов className
.
Лучше всего делегировать событие:
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);
Конечно, я предполагаю, что класс active
должен быть удален после возникновения события mouseout
, вы можете подумать об использовании для него второго делегирования, но вы могли бы просто присоединить обработчик событий к одному элементу который имеет класс 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);
Есть некоторые возможности для улучшения, с этим кодом, но я не собираюсь здесь повеселиться, -).
Ответ 3
Вот метод, адаптированный из JQuery 2.1.1, который принимает элемент dom вместо объекта jquery (поэтому jquery не нужен). Включает проверки типов и выражения регулярных выражений:
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;
};
Ответ 4
getElementsByClassName()
возвращает HTMLCollection, чтобы вы могли попробовать это
var button = document.getElementsByClassName("navButton")[0];
Edit
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");
}
}
Ответ 5
Существует встроенный цикл forEach для массива в 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");
});
Ответ 6
Мне нравится использовать пользовательскую функцию "foreach" для таких вещей:
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;
}
(Не помню, где я нашел указанную выше функцию, но она была весьма полезна.)
Затем после извлечения ваших объектов (до elements
в этом примере) просто сделайте
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
- это простой способ обработки классов элементов. Просто нужна прокладка для нескольких браузеров. Если вы должны использовать setAttribute
, вы должны помнить, что все, что установлено с ним, будет перезаписывать предыдущие значения.
EDIT: Забыл упомянуть, что вам нужно использовать attachEvent
вместо addEventListener
в некоторых версиях IE. Проверить с помощью if ( document.addEventListener ) {...}
.
Ответ 7
Просто добавьте имя класса в начало funciton, а 2-й и 3-й аргументы являются необязательными, и волшебство сделано для вас!
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;
}