如何实现垂直新闻源或时间线,在到达末尾时动态加载新项目并支持跳转到特定位置?

问题描述 投票:0回答:2

我的数据包含数千个我希望能够显示的项目。然而,同时显示所有项目会显着降低性能。另外,我真的只需要一次查看几个项目。

所以基本上,我想要的是像 Facebook 新闻源、Twitter 时间轴或任何即时通讯聊天历史记录视图一样的东西。在所有这些示例中,最初,我只显示了几个项目,通过滚动到列表的末尾,我可以动态加载更多项目。

我还可以跳转到提要的特定位置,例如聊天历史记录的特定日期和时间。提要通常不会加载并显示从现在到指定历史位置的每一条消息,但它会跳转到该位置并显示一些周围的消息。同样,通过到达列表的(任一)末尾,可以动态添加更多项目。

这个功能叫什么?我可以使用哪个库来实现它?

javascript html performance refresh feed
2个回答
0
投票

这是一个小型概念验证,不需要任何第三方库。

在此示例中,数据库由 0 到 500 之间的 501 个项目组成。

FeedEngine
负责从列表中添加/删除项目的机制。实际的站点/项目内容由
itemCallback
函数管理。在这里,它被称为
customItemBuilder
并且非常简单:它只显示项目索引并交替背景颜色。您实际上可以在此处从数据库中提取特定
itemIndex
的内容,并相应地动态调整
itemElement

在移动设备上,您可以滑动浏览项目。在桌面上,最好使用鼠标滚轮滚动浏览提要。

<!DOCTYPE html>
<html lang="en">

<head>
<script>
/**
 * FeedEngine
 *
 * FeedEngine is a vertical news feed or timeline implementation. Initially,
 * only a certain, small amount of items are displayed. If the user reaches
 * either end of the container, for example by scrolling, more and more items
 * are dynamically added to the feed as required. It's also possible to jump
 * to a specific item, i.e. feed position.
 *
 * For each item, an empty, blank DIV element will be added to the container
 * element. Afterwards, a function is called which receives two parameters:
 * `itemElement`, the new element, and `itemIndex`, the index of the new
 * item. This callback function allows you to customize the presentation of
 * the feed items.
 *
 * Options:
 *     containerElement - The element which will contain all DIV elements for
 *         the items. For best results, you should probably choose a DIV
 *         element for the container as well. Furthermore, its CSS should
 *         contain something like `overflow: scroll`. Note: Its attributes
 *         `innerHTML` and `onscroll` will be overwritten.
 *     itemCallback - This function will be called after a new item has been
 *         added to the container. If the callback doesn't return `true`, the
 *         item will immediately be removed again.
 *     moreItemsCount -  The number of new items that will be added above and
 *         below the first item, the target item of a jump or the outermost
 *         item in the feed, respectively.
 *     moreItemsTrigger - The threshold distance to the outermost item which
 *         triggers more items to be added to the feed. For example, if this
 *         option is set to `0`, new items will only be added once the
 *         outermost item is fully in view. Furthermore, a value greater than
 *         or equal to `moreItemsCount` doesn't make sense.
 *     inverseOrder - Use bottom-to-top instead of top-to-bottom order.
 *
 * @constructor
 * @param {Object} options - Options object.
 */
function FeedEngine(options) {
    'use strict';
    this.itemCallback = (itemElement, itemIndex) => {};
    this.moreItemsCount = 20;
    this.moreItemsTrigger = 5;
    this.inverseOrder = false;
    Object.assign(this, options);
    if (this.containerElement === undefined) {
        throw new Error('container element must be specified');
    }
    this.jumpToItem = (itemIndex) => {
        this.containerElement.innerHTML = '';
        this.topItemIndex = itemIndex;
        this.bottomItemIndex = itemIndex;
        var initialItem = this.insertItemBelow(true);
        for (var i = 0; i < this.moreItemsCount; i++) {
            this.insertItemAbove();
            this.insertItemBelow();
        }
        this.containerElement.scrollTop = initialItem.offsetTop - this.containerElement.offsetTop + (this.inverseOrder ? initialItem.clientHeight - this.containerElement.clientHeight : 0);
    };
    this.insertItemAbove = () => {
        this.topItemIndex += this.inverseOrder ? 1 : -1;
        var itemElement = document.createElement('div');
        this.containerElement.insertBefore(itemElement, this.containerElement.children[0]);
        if (!this.itemCallback(itemElement, this.topItemIndex)) {
            itemElement.remove();
        }
        return itemElement;
    };
    this.insertItemBelow = (isInitialItem) => {
        if (isInitialItem === undefined || !isInitialItem) {
            this.bottomItemIndex += this.inverseOrder ? -1 : 1;
        }
        var itemElement = document.createElement('div');
        this.containerElement.appendChild(itemElement);
        if (!this.itemCallback(itemElement, this.bottomItemIndex)) {
            itemElement.remove();
        }
        return itemElement;
    };
    this.itemVisible = (itemElement) => {
        var containerTop = this.containerElement.scrollTop;
        var containerBottom = containerTop + this.containerElement.clientHeight;
        var elementTop = itemElement.offsetTop - this.containerElement.offsetTop;
        var elementBottom = elementTop + itemElement.clientHeight;
        return elementTop >= containerTop && elementBottom <= containerBottom
    };
    this.containerElement.onscroll = (event) => {
        var topTriggerIndex = this.moreItemsTrigger;
        var bottomTriggerIndex = event.target.children.length - this.moreItemsTrigger - 1;
        var topTriggerElement = event.target.children[topTriggerIndex];
        var bottomTriggerElement = event.target.children[bottomTriggerIndex];
        var topTriggerVisible = this.itemVisible(topTriggerElement);
        var bottomTriggerVisible = this.itemVisible(bottomTriggerElement);
        for (var i = 0; i < this.moreItemsCount; i++) {
            if (topTriggerVisible) {
                this.insertItemAbove();
            }
            if (bottomTriggerVisible) {
                this.insertItemBelow();
            }
        }
    };
    this.jumpToItem(0);
}
</script>
</head>

<body>
Feed:
<button onclick="feed = new FeedEngine({containerElement: document.getElementById('container'), itemCallback: customItemBuilder})">top-to-bottom</button>
<button onclick="feed = new FeedEngine({containerElement: document.getElementById('container'), itemCallback: customItemBuilder, inverseOrder: true})">bottom-to-top</button>
<input type="text" id="jump" value="250">
<button onclick="feed.jumpToItem(parseInt(document.getElementById('jump').value))">jump</button>
<div id="container" style="overflow: scroll; width: 300px; height: 100px; resize: both;"></div>
<script>
function customItemBuilder(itemElement, itemIndex) {
    if (0 <= itemIndex && itemIndex <= 500) {
        /* customize the item DIV element here */
        itemElement.innerHTML = 'Content for item index ' + itemIndex;
        itemElement.style.backgroundColor = itemIndex % 2 ? 'LightCyan' : 'LightGray';
        return true;
    }
}
window.onload = () => {
    document.getElementsByTagName('button')[0].click();
}
</script>
</body>

</html>


0
投票

该功能称为无限滚动

© www.soinside.com 2019 - 2024. All rights reserved.