#javascript #dom
#javascript #dom
Вопрос:
Я пытаюсь реализовать getByClassnameHierarchy, но у меня возникают проблемы с реализацией. Должен ли я использовать BFS вместо DFS для обхода узлов?
Может ли кто-нибудь объяснить мне как методы, так и плюсы и минусы их использования?
// Helper function to make output easier to read
const getIds = (elements=[]) => Array.from(elements).map(x => x.id);
/**
* Return all DOM elements who are _leaf_nodes_ that satisfy the hierarchy.
* Hierarchy is a string of class names separated by `>`, akin to
* CSS CHILD SELECTORS.
*
* ex. getByClassnameHierarchy(#root, 'a>b>c') -> [<div class="c" id="c-1"></div>,<div class="c" id="c-2"></div> ]
* "give me all the elements with class name 'c', who have a strict parent with
* class name 'b', who have a strict parent with class name 'a'"
*
* @param root DOMElement: start the search from this DOM element
* @param hierarchy string: `>`-delimited string of classnames
* @return Array<DOMElement>: all DOM elements that satisfy the target hierarchy
*/
function getByClassnameHierarchy(root, hierarchy) {
// parentNode
const res = [];
const level = hierarchy.split('>');
helper(root, level, 0);
return res;
function helper(root, level, cur) {
if(!root) return
if(root.classList amp;amp; root.classList.contains(level[cur-1])){ // b
if(root.parentNode.classList.contains(level[cur-2])) { // a
if(root.classList.contains(level[cur-1])) {
res.push(root);
}
}
} //c
root.childNodes.forEach(child => {
helper(child, level, cur 1);
});
}
}
const root2 = document.getElementById('root2');
// // basic case:
console.log('actual: ', getIds(getByClassnameHierarchy(root2, 'a>b>c')));
console.log(`a>b>c expected:` , `['c-1', 'c-2']`, 'n');
<div id="root2">
<div class="a" id="a-1">
<div class="b" id="b-1">
<div class="c" id="c-1"></div>
<div class="c" id="c-2"></div>
</div>
</div>
</div>
Проблема:
Ожидаемый возврат: [ 'b-1', 'c-1', 'c-2' ]
вместо ['c-1', 'c-2']
Не уверен, где я ошибаюсь.
Комментарии:
1. Пожалуйста, отредактируйте свой образец, чтобы сделать его работоспособным (с добавлением некоторого количества HTML).
2. @Jeto Готово:D Я добавил код.
3. Я имею в виду, что вы могли бы просто сделать
getByClassnameHierarchy(root, hierarchy) { return root.querySelectorAll("." hierarchy.replace(/>/g,'>.'));}
и покончить с этим…4. @NiettheDarkAbsol весь смысл того, что я делаю это, заключался бы в его реализации.. Я не хочу использовать querySelectorAll. Меня спрашивали об этом в интервью. Спасибо за ответ.
Ответ №1:
правка 2020 года (благодаря комментарию @techguy2000):
Эта версия должна корректно обрабатывать случаи, когда сам корневой элемент имеет некоторые классы. Это также исправляет несколько проблем с предыдущей версией:
const getIds = (elements = []) => Array.from(elements).map(x => x.id);
function getByClassnameHierarchy(element, hierarchy, level = 0) {
const result = [];
const classNames = hierarchy.split('>');
const currentClassName = classNames[0];
const isClassFound = element.classList amp;amp; element.classList.contains(currentClassName);
const isLastClass = classNames.length === 1;
if (isClassFound) {
if (isLastClass) {
result.push(element);
} else {
element.childNodes.forEach(child => {
result.push(...getByClassnameHierarchy(child, classNames.slice(1).join('>'), level 1));
});
}
}
if (level === 0) {
element.childNodes.forEach(child => {
result.push(...getByClassnameHierarchy(child, hierarchy, level));
});
}
return resu<
}
// Test
const root2 = document.getElementById('root2');
console.log('a>b>c actual:', getIds(getByClassnameHierarchy(root2, 'a>b>c')));
console.log('a>b>c expected:', ['c-1', 'c-2', 'c-3', 'c-4']);
console.log('b>c actual: ', getIds(getByClassnameHierarchy(root2, 'b>c')));
console.log('b>c expected:', ['c-1', 'c-2', 'c-3', 'c-4']);
console.log('b actual: ', getIds(getByClassnameHierarchy(root2, 'b')));
console.log('b expected:', ['b-1', 'b-2']);
console.log('a>c actual: ', getIds(getByClassnameHierarchy(root2, 'a>c')));
console.log('a>c expected:', []);
<div class="a" id="root2">
<div class="a" id="a-1">
<div class="b" id="b-1">
<div class="c" id="c-1"></div>
<div class="c" id="c-2"></div>
</div>
</div>
<div class="b" id="b-2">
<div class="c" id="c-3"></div>
<div class="c" id="c-4"></div>
</div>
</div>
Первоначальный ответ:
Вы могли бы сделать что-то вроде этого (подробности см. в комментариях):
const getIds = (elements = []) => Array.from(elements).map(x => x.id);
function getByClassnameHierarchy(root, hierarchy, level = 0) {
let result = [];
// Grab the class names
const classNames = hierarchy.split('>');
// Retrieve the current (first) one
const currentClassName = classNames[0];
// For each child
root.childNodes.forEach(child => {
// If it contains the given class
if (child.classList amp;amp; child.classList.contains(currentClassName)) {
// Append the result of the following selector on this child,
// or the child itself if we're at the last part of the selector
result = result.concat(classNames.length > 1
? getByClassnameHierarchy(child, classNames.slice(1).join('>'), level 1)
: child);
}
// Otherwise, if we're still on the first part of the selector,
// append the result of the same selector on this child
else if (level === 0) {
result = result.concat(getByClassnameHierarchy(child, hierarchy, level));
}
});
return resu<
}
// Test
const root2 = document.getElementById('root2');
console.log('a>b>c actual:', getIds(getByClassnameHierarchy(root2, 'a>b>c')));
console.log('a>b>c expected:', ['c-1', 'c-2']);
console.log('b>c actual: ', getIds(getByClassnameHierarchy(root2, 'b>c')));
console.log('b>c expected:', ['c-1', 'c-2', 'c-3', 'c-4']);
console.log('b actual: ', getIds(getByClassnameHierarchy(root2, 'b')));
console.log('b expected:', ['b-1', 'b-2']);
console.log('a>c actual: ', getIds(getByClassnameHierarchy(root2, 'a>c')));
console.log('a>c expected:', []);
<div id="root2">
<div class="a" id="a-1">
<div class="b" id="b-1">
<div class="c" id="c-1"></div>
<div class="c" id="c-2"></div>
</div>
</div>
<div class="b" id="b-2">
<div class="c" id="c-3"></div>
<div class="c" id="c-4"></div>
</div>
</div>
Комментарии:
1. Всегда пожалуйста. Я только что добавил еще несколько примеров ниже функции, дайте мне знать, если это сработает для вас.
2. Итак, мы выполняем DFS .. Можем ли мы сделать то же самое путем обхода порядка уровней? (путем поддержания очереди?) Просто любопытно, каковы были бы «ЗА» и «Против» при таком подходе!
3. @TechnoCorner Я немного незнаком с этими понятиями (я просто погуглил термины, потому что я их не запомнил). Я уверен, что существуют разные подходы к этому, но этот должен быть простым и достаточно производительным. В статье, которую я только что прочитал , говорится, что «если наша проблема заключается в поиске чего-то, что, скорее всего, ближе к root, мы бы предпочли BFS. И если целевой узел находится близко к листу, мы бы предпочли DFS.». Так что я предполагаю, что DFS здесь подойдет, поскольку, скорее всего, он довольно глубоко внедрен в DOM (по крайней мере, может).
4. Спасибо, я дочитаю статью до конца. Кроме того, похоже, что этот тест, похоже, не выполняется: // пробелы в селекторе не должны ничего возвращать: console.log(‘actual: ‘, getIds(getByClassnameHierarchy(root2, ‘a> c’))); console.log(
a>c expected:
,[]
, ‘ n’);5. При использовании
a>c
он возвращает[ 'c-1', 'c-2' ]
, но фактическое значение равно[]