Если у меня есть такой список:
<ul id = "mylist">
<li id = "list-item1">text 1</li>
<li id = "list-item2">text 2</li>
<li id = "list-item3">text 3</li>
<li id = "list-item4">text 4</li>
</ul>
Какой самый простой способ переупорядочить узлы DOM в соответствии с моими предпочтениями? (Это должно происходить автоматически, когда страница загружается, предпочтение порядка списка берется из файла cookie)
Например.
<ul id = "mylist">
<li id = "list-item3">text 3</li>
<li id = "list-item4">text 4</li>
<li id = "list-item2">text 2</li>
<li id = "list-item1">text 1</li>
</ul>



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Хотя, вероятно, есть более простой способ сделать это с помощью библиотеки JS, вот рабочее решение с использованием vanilla js.
var list = document.getElementById('mylist');
var items = list.childNodes;
var itemsArr = [];
for (var i in items) {
if (items[i].nodeType == 1) { // get rid of the whitespace text nodes
itemsArr.push(items[i]);
}
}
itemsArr.sort(function(a, b) {
return a.innerHTML == b.innerHTML
? 0
: (a.innerHTML > b.innerHTML ? 1 : -1);
});
for (i = 0; i < itemsArr.length; ++i) {
list.appendChild(itemsArr[i]);
}
Спасибо за ваше решение, NickF, я собираюсь немного его адаптировать, поскольку я использую jQuery, но я думаю, что теперь у меня есть идея, как это сделать, спасибо! :)
Используя list.children вместо list.childNodes, вы можете избежать проверки текстовых узлов.
Когда я впервые прочитал этот ответ, я подумал, что vanilla js? Почему здесь требуется vanilla.js? : ^)
@LivingstonSamuel ... но за счет того, что он не работает в IE (даже IE11!)
Вы можете обнаружить, что сортировка узлов DOM не работает. Другой подход состоял бы в том, чтобы иметь в вашем javascript массив, представляющий данные, которые будут поступать в узлы DOM, отсортировать эти данные, а затем повторно создать div, содержащий узлы DOM.
Может быть, у вас не так много узлов для сортировки, поэтому это не имеет значения. Мой опыт основан на попытке отсортировать HTML-таблицы, манипулируя DOM, включая таблицы с сотнями строк и парой десятков столбцов.
Если вы уже используете jQuery, я бы порекомендовал tinysort: http://tinysort.sjeiti.com/
$("li").tsort({order:"asc"});
$("li").tsort({order:"desc"});
Tinysort в несколько раз медленнее, чем 13 строк кода, на основе сортировки результата выбора jquery и его повторного добавления - jsperf.com/attr-vs-getattribute/12
TinySort раньше был плагином jQuery, но был переписан, чтобы удалить зависимость jQuery. Теперь он меньше и быстрее
не анализируя слишком много, если это приносит что-то новое в таблицу, я обычно использую это:
function forEach(ar, func){ if (ar){for(var i=ar.length; i--; ){ func(ar[i], i); }} }
function removeElement(node){ return node.parentNode.removeChild(node); }
function insertBefore(ref){ return function(node){ return ref.parentNode.insertBefore(node, ref); }; }
function sort(items, greater){
var marker = insertBefore(items[0])(document.createElement("div")); //in case there is stuff before/after the sortees
forEach(items, removeElement);
items.sort(greater);
items.reverse(); //because the last will be first when reappending
forEach(items, insertBefore(marker));
removeElement(marker);
}
где элемент - это массив потомков одного и того же родителя. мы удаляем начало с последнего и добавляем, начиная с первого, чтобы избежать мерцания в верхней части, которая, вероятно, находится на экране. Я обычно получаю свой массив элементов следующим образом:
forEachSnapshot(document.evaluate(..., 6, null), function(n, i){ items[i] = n; });
Посмотреть в действии: http://jsfiddle.net/stefek99/y7JyT/
jQuery.fn.sortDomElements = (function() {
return function(comparator) {
return Array.prototype.sort.call(this, comparator).each(function(i) {
this.parentNode.appendChild(this);
});
};
})();
Лаконичный
Хороший! Мне также нравится лаконичность, поэтому на основе этого я придумал очень простая функция jQuery, который позволяет вам предоставить функцию обратного вызова, которая вычисляет значение для сортировки. (Я включил два примера: сортировка списка по алфавиту и сортировка таблицы по числовому значению в первом столбце - оба, по сути, однострочные.)
Я знаю, что это старый пост, но могу ли я спросить, в чем преимущество использования самоисполняющейся функции, возвращающей функцию sortDomElements? Не было бы проще сказать jQuery.fn.sortDomElements = function (сравнитель) ...? вот так jsfiddle.net/y7JyT/77
Моя версия, надеюсь будет полезна другим:
var p = document.getElementById('mylist');
Array.prototype.slice.call(p.children)
.map(function (x) { return p.removeChild(x); })
.sort(function (x, y) { return /* your sort logic, compare x and y here */; })
.forEach(function (x) { p.appendChild(x); });
Что значит /* sort logic */? : / Код выглядит неплохо, но моя логика сортировки слабовата
Это означает, что вы должны поместить туда свою логику сортировки, взглянуть на developer.mozilla.org/en/docs/Web/JavaScript/Reference/…, в основном это то же самое, только x и y являются узлами DOM.
Вот функция ES6 для сортировки узлов DOM на месте:
const sortChildren = ({ container, childSelector, getScore }) => {
const items = [...container.querySelectorAll(childSelector)];
items
.sort((a, b) => getScore(b) - getScore(a))
.forEach(item => container.appendChild(item));
};
Вот как вы могли бы использовать его для сортировки Неизвестные отзывы пользователей по баллам:
sortChildren({
container: document.querySelector("#main-stream"),
childSelector: ".item",
getScore: item => {
const rating = item.querySelector(".rating");
if (!rating) return 0;
const scoreString = [...rating.classList].find(c => /r\d+/.test(c));
const score = parseInt(scoreString.slice(1));
return score;
}
});
Используйте синтаксис es6 для обращения к детям:
var list = document.querySelector('#test-list');
[...list.children]
.sort((a,b)=>a.innerText>b.innerText?1:-1)
.forEach(node=>list.appendChild(node));
хороший! это чистый способ сделать это.
Блестяще! Самый умный способ изменить порядок узлов
Это лучшее решение
Семантически более корректно использовать метод «forEach» вместо «map», поскольку в этом случае вас не интересует фактическое сопоставление элементов, вы просто хотите вызвать обратный вызов с побочным эффектом (присоединение к другому элемент).
Самый изящный способ, который я могу придумать:
Параметр compare аналогичен функции сравнения, используемой в Array.sort ().
Отсортируйте дочерние узлы.
/**
* @param {!Node} parent
* @param {function(!Node, !Node):number} compare
*/
function sortChildNodes(parent, compare) {
const moveNode = (newParent, node) => {
// If node is already under a parent, append() removes it from the
// original parent before appending it to the new parent.
newParent.append(node);
return newParent;
};
parent.append(Array.from(parent.childNodes) // Shallow copies of nodes.
.sort(compare) // Sort the shallow copies.
.reduce(moveNode, document.createDocumentFragment()));
}
Отсортируйте дочерние элементы (подмножество дочерних узлов).
/**
* @param {!Element} parent
* @param {function(!Element, !Element):number} compare
*/
function sortChildren(parent, compare) {
const moveElement = (newParent, element) => {
// If element is already under a parent, append() removes it from the
// original parent before appending it to the new parent.
newParent.append(element);
return newParent;
};
parent.append(Array.from(parent.children) // Shallow copies of elements.
.sort(compare) // Sort the shallow copies.
.reduce(moveElement, document.createDocumentFragment()));
}
Я не уверен, что это поможет вам, но если вы используете серверную технологию (например, PHP) на этой странице, то она также должна иметь доступ к файлам cookie. в PHP - в глобальном массиве $ _COOKIE.