Просто чтобы продолжить, я основал свой код на реализации Роберта Наймана, опубликованной Shog9, но немного отошел от его точной версии по трем причинам:
- Он позволил вам выбрать корневой элемент и тип тега для фильтрации ваших результатов. Мне не нужна эта функциональность, поэтому, удалив ее, я смог значительно упростить код.
- Первое, что делает его код, это проверяет, существует ли рассматриваемая функция, и если он это делает, он все равно предоставляет свою собственную реализацию. Это только казалось ... странным. Я понимаю, что он добавляет функциональность к оригиналу, но опять же: я не использую эти функции.
- Я хотел, чтобы дополнительный кусочек синтаксического сахара мог вызывать его так, как я бы назвал
document.getElementById()
или document.getElementsByTagName()
.
Обратите внимание, что я все еще полагался в основном на его код. Его навыки работы с javascript явно превосходят мои собственные. Я пытался выделить некоторые избыточные переменные, но это все.
Имея это в виду, вот что я закончил ( кажется для работы в IE6, IE7, Firefox 3 и Chrome см. Новую заметку в конце ):
if (!document.getElementsByClassName)
document.getElementsByClassName = function (className)
{
var classes = className.split(" ");
var classesToCheck = "";
var returnElements = [];
var match, node, elements;
if (document.evaluate)
{
var xhtmlNamespace = "http://www.w3.org/1999/xhtml";
var namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace:null;
for(var j=0, jl=classes.length; j<jl;j+=1)
classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
try
{
elements = document.evaluate(".//*" + classesToCheck, document, namespaceResolver, 0, null);
}
catch(e)
{
elements = document.evaluate(".//*" + classesToCheck, document, null, 0, null);
}
while ((match = elements.iterateNext()))
returnElements.push(match);
}
else
{
classesToCheck = [];
elements = (document.all) ? document.all : document.getElementsByTagName("*");
for (var k=0, kl=classes.length; k<kl; k+=1)
classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
for (var l=0, ll=elements.length; l<ll;l+=1)
{
node = elements[l];
match = false;
for (var m=0, ml=classesToCheck.length; m<ml; m+=1)
{
match = classesToCheck[m].test(node.className);
if (!match) break;
}
if (match) returnElements.push(node);
}
}
return returnElements;
}
Обновление:
Одна новая заметка по этому вопросу. С тех пор я перечитал заметки об исходной реализации, и теперь я понимаю, что мой код может упасть в случае, когда существующий браузер имеет свою собственную реализацию, потому что реализации по умолчанию возвращают список узлов, где это возвращает массив. Это включает в себя более поздние браузеры Firefox, Safari и Opera. В большинстве случаев это не имеет значения, но в некоторых ситуациях это может иметь значение. Это объясняет пункт № 2 из списка выше.
Это означает, что хотя мой код технически работает везде, это может привести к слегка различному (читай: трудно отладить) поведению в разных местах, и это не хорошо. Я должен исправить это, либо либо вернуть список узлов, либо переопределить предоставленный метод для возврата массива (что и сделал оригинал). Возможно, первое будет проще, но последнее будет лучше .
Однако в настоящий момент он работает в локальной интрасети (почти во всех IE), поэтому пока я оставлю исправление в качестве упражнения для читателя.