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?
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>
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