Let's imagine we have an HTML message list. Each item has index - Unix timestamp with milliseconds.
<ul>
<li ts="1657744925386">Item 1</li>
<li ts="1657744941061">Item 2</li>
<li ts="1657744953364">Item 3</li>
<li ts="1657744964372">Item 4</li>
<li ts="1657744973219">Item 5</li>
<li ts="1657744978450">Item 6</li>
</ul>
Then we have to insert another item, with ts=1657744950123. It should be after Item 3.
Is there an efficient way to calculate the new position in the list so that jQuery can insert the new element (because it requires a reference element relative to which will be inserted after or before the new element), given that there can be a lot of messages on the page and we don't have possibility to walk through all of them every time, making comparisons?
CodePudding user response:
Assuming no existing data structure representing the list, I'd loop over the list elements, checking the attribute value, until I found the first larger value. That's the index 1 at which you'll insert.
If there is a data array, you could do something similar on the array itself and regenerate the list.
Here's a wild 'n woolly example of the former approach. If you need the text nodes to also be updated, you probably want to take approach #2 and rebuild the list programmatically.
const list = document.querySelector('ul');
const items = list.querySelectorAll('li');
const newItemVal = 1657744950123;
let itemAfterIndex;
for (const [index, item] of items.entries()) {
const tsVal = parseInt(item.getAttribute('ts'));
if (tsVal > newItemVal) {
itemAfterIndex = index;
break;
}
}
const newItem = document.createElement('li');
newItem.setAttribute('ts', newItemVal);
newItem.textContent = 'New item';
const itemAfter = list.querySelectorAll('li')[itemAfterIndex];
list.insertBefore(newItem, itemAfter);
<ul>
<li ts="1657744925386">Item 1</li>
<li ts="1657744941061">Item 2</li>
<li ts="1657744953364">Item 3</li>
<li ts="1657744964372">Item 4</li>
<li ts="1657744973219">Item 5</li>
<li ts="1657744978450">Item 6</li>
</ul>
CodePudding user response:
Consider the following jQuery example.
$(function() {
var myNewItem = $("<li>", {
ts: 1657744950123
}).html("Item 7");
$("ul li").each(function(i, el) {
console.log($(el).attr("ts"), myNewItem.attr("ts"));
if ($(el).attr("ts") > myNewItem.attr("ts")) {
myNewItem.insertBefore(el);
return false;
}
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li ts="1657744925386">Item 1</li>
<li ts="1657744941061">Item 2</li>
<li ts="1657744953364">Item 3</li>
<li ts="1657744964372">Item 4</li>
<li ts="1657744973219">Item 5</li>
<li ts="1657744978450">Item 6</li>
</ul>
This assumes the list is already sorted. If not, you can append the new item anyplace, detach all the items, sort them, and append them back.
$(function() {
$("<li>", {
ts: 1657744950123
}).html("Item 7").appendTo("ul");
var myItems = $("ul li").detach();
myItems.sort(function(a, b) {
if ($(a).attr("ts") < $(b).attr("ts")) {
return -1;
}
if ($(a).attr("ts") > $(b).attr("ts")) {
return 1;
}
if ($(a).attr("ts") == $(b).attr("ts")) {
return 0;
}
});
$("ul").append(myItems);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li ts="1657744925386">Item 1</li>
<li ts="1657744941061">Item 2</li>
<li ts="1657744953364">Item 3</li>
<li ts="1657744964372">Item 4</li>
<li ts="1657744973219">Item 5</li>
<li ts="1657744978450">Item 6</li>
</ul>
See more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort