【问题标题】:Horizontal data update is not working on scroll水平数据更新不适用于滚动
【发布时间】:2013-08-19 14:16:24
【问题描述】:

我有一个大数组,我需要将它渲染成一个表格。我不是渲染所有项目,而是水平和垂直地渲染少数项目。然后基于鼠标滚动滚动是否发生垂直/水平更新表值。但是我在这方面有两个问题

我的代码有问题

  1. 水平滚动时数据不会更新。
  2. 水平滚动也会闪烁屏幕。
  3. 水平元素未正确对齐。

这里是jsbin链接http://jsbin.com/oSOsIQe/2/edit

这是JS代码,我知道代码不干净,但我会稍后清理它。

var $matrix = (function () {
function $matrix(data, holder, hidescrollbar, config) {
    var header_h = config.header || "150px";
    var data_h = config.header || "90px";
    !data && (function () {
        // Fake Data, will be removed later
        data = new Array(50000);
        for (var i = 0, l = data.length; i < l; i++) {
            var dummy = Math.random().toString(36).substring(5);
            var dum = [];
            for (var j = 0; j < 26; j++) {
                dum.push(dummy + i);
            }
            data[i] = dum;
        }
    }());
    hidescrollbar = hidescrollbar || false;
    var heightForcer = holder.appendChild(document.createElement('div'));
    heightForcer.id = "heightForcer";
    var view = null;
    //get the height of a single item
    var dimensions = (function () {
        //generate a fake item and calculate dimensions of our targets
        var div = document.createElement('div');
        div.style.height = "auto";
        div.innerHTML = "<div class='rowHeader'>fake</div><div class='rowData'>fake</div>";
        holder.appendChild(div);
        var output = {
            row: div.firstChild.offsetHeight,
            header: div.firstChild.offsetWidth,
            data: div.lastChild.offsetWidth
        }
        holder.removeChild(div);
        return output;
    })();

    function refreshWindow() {
        //remove old view
        if (view != null) {
            view.innerHTML = "";
        } else {
            //create new view
            view = holder.appendChild(document.createElement('div'));
        }
        var firstItem = Math.floor(holder.scrollTop / dimensions.row);
        var lastItem = firstItem + Math.ceil(holder.offsetHeight / dimensions.row) + 1;
        if (lastItem + 1 >= data.length) lastItem = data.length - 1;

        var hfirstItem = Math.floor(holder.scrollLeft / dimensions.data);
        var hlastItem = hfirstItem + Math.ceil(holder.offsetWidth / dimensions.data) + 1;
        if (hlastItem + 1 >= data[firstItem].length) hlastItem = data[firstItem].length - 1;

        //position view in users face
        view.id = 'view';
        view.style.top = (firstItem * dimensions.row) + 'px';
        view.style.left = (hfirstItem * dimensions.data) + 'px';
        var div;
        //add the items
        for (var index = firstItem; index <= lastItem; ++index) {
            div = document.createElement('div');
            var curData = data[index].slice(hfirstItem, hlastItem);
            div.innerHTML = '<div class="rowHeader">' + 
              curData.join('</div><div class="rowData">') +
            "</div>";
            div.className = "listItem";
            div.style.height = dimensions.row + "px";
            view.appendChild(div);
        }
        console.log('viewing items ' + firstItem + ' to ' + lastItem);
    }
    heightForcer.style.height = (data.length * dimensions.row) + 'px';
    heightForcer.style.width = (data[0].length * dimensions.data) + 'px';
    if (hidescrollbar) {
        //work around for non-chrome browsers, hides the scrollbar
        holder.style.width = (holder.offsetWidth * 2 - view.offsetWidth) + 'px';
    }
    refreshWindow();

    function delayingHandler() {
        //wait for the scroll to finish
        //setTimeout(refreshWindow, 10);
        refreshWindow();
    }
    if (holder.addEventListener) holder.addEventListener("scroll", delayingHandler, false);
    else holder.attachEvent("onscroll", delayingHandler);
}
return $matrix;
 }());

new $matrix(undefined, document.getElementById('listHolder'), false, {
  header: "150px",
  data: "90px",
  headerColumns: 2
});

请帮帮我。

【问题讨论】:

  • 投票结束这个问题的兄弟们,请发表评论,以便其他人也能有所了解。对于不了解 javascript 并投票结束这个问题的人来说,这是 JS 问题。请不要认为 jQuery 是一种语言,Javascript 是一种语言。

标签: javascript


【解决方案1】:

SOLUTIONmy answer 对类似问题的概念扩展

代码

function matrix(data, holder, config) {
    'use strict';

    //copy the config, substituting defaults
    config = {
        cellWidth : (config && config.cellWidth) || 150,
        rowHeight : (config && config.rowHeight) || 22,
    };

    if (!data) {
        //create 50000x26 array for data
        data = (function (length, depth) {
            var output = new Array(length);
            var startAt;

            for (var index = 0; index < length; ++index) {
                //var startAt = Math.random().toString(36).substring(5);
                var startAt = index + ':';
                output[index] = new Array(depth);

                for (var index2 = 0; index2 < depth; ++index2)
                    output[index][index2] = startAt + index2;
            }

            return output;
        })(50000, 26);
    }

    //guard against 0 length arrays
    if (data.length < 1 || data[0].length < 1)
        return;

    var areaForcer = holder.appendChild(holder.ownerDocument.createElement('div'));

    var view = null;
    function refreshWindow() {
        //remove old view
        if (view != null)
            view.innerHTML = "";
        //create new view
        else
            view = holder.appendChild(holder.ownerDocument.createElement('div'));

        var firstRow = Math.floor(holder.scrollTop / config.rowHeight);
        var lastRow = firstRow + Math.ceil(holder.offsetHeight / config.rowHeight) + 1;
        if (lastRow + 2 > data.length)
            lastRow = data.length - 1;

        var firstColumn = Math.floor(holder.scrollLeft / config.cellWidth);
        var lastColumn = firstColumn + Math.ceil(holder.offsetWidth / config.cellWidth) + 1;
        if (lastColumn + 2 > data[0].length)
            lastColumn = data[0].length - 1;

        //position view in users face
        view.id = 'view';
        view.style.top = (firstRow * config.rowHeight) + 'px';
        view.style.left = (firstColumn * config.cellWidth) + 'px';

        var row;
        var cell;
        //add the rows
        for (var index = firstRow; index <= lastRow; ++index) {
            row = view.ownerDocument.createElement('div');
            row.style.height = config.rowHeight - 2 + 'px';
            view.appendChild(row);

            //add the cells
            for (var index2 = firstColumn; index2 <= lastColumn; ++index2) {
                cell = row.ownerDocument.createElement('div');
                cell.className = 'listItem';
                cell.innerHTML = data[index][index2];
                cell.style.width = config.cellWidth - 2 + 'px';
                row.appendChild(cell);
            }
        }

        console.log('viewing items [' + firstRow + ':' + lastRow + '][' + firstColumn + ':' + lastColumn + ']');
    }

    areaForcer.style.height = (data.length * config.rowHeight) + 'px';
    areaForcer.style.width = (data[0].length * config.cellWidth) + 'px';

    refreshWindow();

    function delayingHandler() {
        //wait for the scroll to finish
        setTimeout(refreshWindow, 10);
    }

    if (holder.addEventListener)
        holder.addEventListener('scroll', delayingHandler, false);
    else
        holder.attachEvent('onscroll', delayingHandler);
}

matrix(null, document.getElementById('listHolder'), false);
html, body {
    width:100%;
    height:100%;
    padding:0;
    margin:0
}

body{
    overflow:hidden; 
}

.listItem {
    border : 1px solid gray;
    margin : 1px 0px;
    display : inline-block;
}

#listHolder {
    position:relative;
    height:100%;
    width:100%;
    background-color:#CCC;
    box-sizing:border-box;
    overflow:auto;
}

#view {
    position:absolute;
}

#view, #view * {
    overflow : hidden;
    white-space : nowrap;
}

#view > div {
    width : 100%;
}
<div id="listHolder"></div>

只是一些事情来清理你的代码:

  • 您没有以 OO 方式使用对象,不需要 new/将函数用作构造函数(如果要使用,请以大写开头)

  • 在这个时代,编译器可以为我们做很多事情,没有理由使用像 'i' 或 'j' 这样的名称,其中 'index' 更容易解释,以及 if 语句是为您使用 and 运算符而制作的(!data &amp;&amp; /*set data*/,使用 if (!data) /*set data*/

  • 不要在不需要的地方使用匿名的一次性调用函数,每次父函数运行时都需要重新编译它们

  • 在循环外声明变量

  • 除非代码正常工作,否则不要使用 random()(在这种情况下),这样会更难看到发生了什么

  • 个人偏好将 null 发送给函数而不是 undefined,并且当矩阵同样合适时,不要将函数命名为 $matrix 使用 cmets!

  • 个人喜好除非您知道自己在做什么,否则 ++x 正是您所需要的,而 x++ 只是对资源的轻微浪费

  • 因为您使用的是水平滚动,所以忘记隐藏滚动条

  • 从简单开始,不要试图获得高度,强行增加。诸如检测高度之类的东西是为工作解决方案保留的细节

  • 始终使用正确的缩进,即使 if 语句可以写在一行上。 您的个人偏好无关紧要,因为您在网上发布代码希望其他人帮助您

  • 当您编辑代码时,请确保更新语义。例如。名为“heightForcer”的元素现在也可以控制宽度,通过将其称为“areaForcer”来避免这种情况

【讨论】:

  • 很高兴收到您的答复!
  • 谁能拒绝一个复杂的问题?还有这么高的赏金! :P 感谢您对我上一个答案的评论提出警告
  • 我有点想知道,我可以知道我的代码有什么问题以及您在哪里更正了它,除了我只想知道发生逻辑不匹配的那些点。你能帮帮我吗
  • 它可能是任意数量的东西。查看您的原始代码,您似乎已经以与我大致相同的方式扩展了功能(查看refreshWindow() 内部)。它很可能只是归结为 HTML/CSS 问题,如果配置不正确,元素本身将无法运行。我确实注意到的一件事是您从delayingHandler() 中删除了延迟,这肯定会使它的行为有点奇怪(但不是非工作状态)
  • 如果您编写的代码易于阅读(缩进和代码约定)以及使用使代码几乎不言自明的逻辑和名称;问题会更容易被发现。充分利用浏览器中的开发人员工具,这就是我为实现上述工作所做的工作
【解决方案2】:

问题是您将onmousewheel 事件与#foo 挂钩,但没有意识到#foo 仅与它包含的数据一样大。如果您进行以下更改(即使用#wrapper 而不是#foo

var listHold = document.getElementById('wrapper');

您会注意到该值确实得到了更新。

【讨论】:

  • 我已根据您的建议更新了代码以及鼠标处理程序代码。还是没有运气。
  • 抱歉,共享的代码有误。显然,这意味着您必须对代码进行一些重构。
  • 请您花点时间准备一把小提琴。
  • 在垂直滚动工作之前,主要问题是水平滚动和更新滚动位置
  • 奇怪,对我来说垂直不起作用,所以我做了这个改变。顺便说一句,你如何使用鼠标水平滚动?
【解决方案3】:

我注意到您的 jsbin 示例已稍作修改,并且使用了 wheelDelta 属性。这就是为什么您无法区分水平更新和垂直更新的原因。您需要使用wheelDeltaXwheelDeltaY,这将是一个值或零,这取决于用户是垂直滚动还是水平滚动。

另外,请小心使用鼠标滚轮事件,它不符合标准,因此可能会再次困扰您。可能值得看一下 JQuery 源代码,以了解 scroll() 方法如何实现这些处理程序。我想有一个计时器可以监视元素的滚动偏移量,它会在对象滚动时触发合成事件。但这是一个完全疯狂的猜测。就像我说的,你最好看看它。

【讨论】:

  • 感谢您在 wheelDeltaX 上教育我,在我使用之前 `var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); ` 来识别滚动的顶部和底部,但是当我瞄准水平滚动时,我也切换到scrollTopscrollLeft 方法。
猜你喜欢
  • 2021-02-03
  • 2022-01-04
  • 2020-02-20
  • 2018-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多