Horizontal data update is not working on scroll
Asked Answered
I

3

12

I have a big array and I need to render it into a table. Instead of rendering all items I am rendering only few items horizontally and vertically. Then on scroll based on mouse scroll whether happened vertical / horizontal updating table values. But I have two problems in this

Here are problems with my code

  1. When scrolling horizontally data is not updated.
  2. Scrolling horizontally is flickering the screen also.
  3. Horizontal elements are not aligned properly.

Here is the jsbin link http://jsbin.com/oSOsIQe/2/edit

Here is the JS code, I know code is not clean but I will clean it later.

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
});

Please help me on this.

Inkberry answered 17/8, 2013 at 6:12 Comment(1)
Dudes who voted to close this question, please post a comment so that others will also get an idea. And for people who does not know javascript and vote to close this question, This is JS question. Please do not think jQuery is a language, Javascript is a language.Inkberry
E
1

SOLUTION, an conceptual extension to my answer to a similar question

CODE

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>

Just some things to clean up your code:

  • you're not using the object in an OO fashion, no need for new/use the function as a constructor (and if you are going to, start it with a capital)

  • in this day and age the compiler can do a lot of things for us, there's no reason to use names like 'i' or 'j' where 'index' is much more self explanatory, and the if statement was made for what you used the and operator for (!data && /*set data*/, use if (!data) /*set data*/)

  • don't employ anonymous, call-once functions where they're not required, they need to be recompiled each time the parent function runs

  • declare variables outside of loops

  • don't use random() (in this case) unless the code is working, makes it harder to see whats going on

  • personal preference send null to a function instead of undefined and don't name a function $matrix when matrix is just as apt use comments!

  • personal preference unless you know what you're doing, ++x is exactly what you need and x++ is a minor waste of resources

  • because you're using horizontal scroll, forget about hiding the scrollbar

  • start out simple and don't attempt to get the height, just force one. things like detecting height are niceties reserved for working solutions

  • always use proper indentation, even if the if statement can be written on one line. Doesn't matter about your personal preference because you're posting the code online expecting other people to help you

  • when you edit code make sure you update the semantics. E.g. the element called 'heightForcer' now manipulates the width aswell, obviate that by calling it 'areaForcer'

Emilieemiline answered 21/8, 2013 at 0:58 Comment(10)
Glad to hear an answer from you!Inkberry
who can turn down a complex problem? and with such a high bounty too! :P thanks for the heads up with that comment you placed on my last answerEmilieemiline
I am bit wondering, may I know what was the problem with my code and where you have corrected it, except those points I just wanted to know logical mismatch happened. Could you please help meInkberry
it could have been any number of things. looking at your original code you seem to have extended the functionality in much the same way I had (looking inside refreshWindow()). It may well have just come down to a HTML/CSS issue, where the elements themselves won't behave if not configured correctly. One thing I did notice was that you removed the delay from delayingHandler(), which definitely would have made it behave a little odd (but not to a non-working state)Emilieemiline
There is a huge gap observed at the end of vertical scroll area. And when using cellWidth how to employ variable cellWidths in row. For example we will have row headers at the left, If I want to fix two columns at the left and remaining cells will be changing. Exactly like freezed columns and freezed rows. While employing freezed panes I may have to give variables widths to fixed rows and actual data rows. How to proceed with that? Could you please help me to get an idea of it.Inkberry
if you write code that's easy to read (indentation, and code conventions) as well as use logic and names that make the code almost self explanatory; problems will be easier to uncover. Make good use of the developer tools within the browser too, that's what I did to get the above workingEmilieemiline
please see my last comment.Inkberry
yes I noticed that issue too, I'm at work though and can't spend more time here as is. This should definitely push you in the right direction. It's a fiddle, so you can fiddle with it as much as you want. If anything goes wrong, inspect elements with the browser's dev-tools and try to figure out what's wrong. Don't give up, this is where the majority of learning happensEmilieemiline
I am facing problems while implementing fixed headers and fixed columns with this approach!Inkberry
I don't understand what you mean. What problems, what are you referring to when you say "fixed ..." etc? You may have to post a fiddle, or better yet, a new question (about the specific problem, not what you want done). NB not only are comments not the best place to converse, but StackOverflow isn't the best place for people to ask for whole solutions. It's a QA site, not a programmer outsourcing agency.Emilieemiline
C
2

I've noticed that your jsbin example has been modified slightly and is using the wheelDelta attribute. This is why you are not able to distinguish between updating horizontally and vertically. You need to use wheelDeltaX and wheelDeltaY, which will wither be a value or zero, depending on whether the user scrolled vertically or horizontally.

Also, be careful using the mousewheel event, it is not standards compliant so may come back to haunt you. Might be worth having a look at the JQuery source to see how the scroll() method implements these handlers. I would imagine there is a timer that monitors the scroll offsets of the element, which triggers synthetic events when the object scrolls. But that is a complete wild guess. Like I said, you are better off having a look at it.

Cookbook answered 19/8, 2013 at 8:3 Comment(1)
Thanks for educating me on wheelDeltaX, Before I was using ` var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));` to identify the scroll top and bottom, but as I am targeting horizontal scroll also I switched to scrollTop, scrollLeft method.Inkberry
D
1

The problem is that you are hooking your onmousewheel event on #foo but don't realize that #foo is only as big as the data it contain. If you make the following change (i.e. take #wrapper instead of #foo)

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

You'll notice that the value do get updated.

Dauphin answered 19/8, 2013 at 7:49 Comment(5)
I have updated the code as per your suggestion and also mousehandler code. Still no luck.Inkberry
Sorry, wrong piece of code shared. Obviously, it means you'll have to refactor your code a bit.Dauphin
Could you please take sometime to prepare a fiddle.Inkberry
before also vertical scrolling was working, the main problem was with horizontal scrolling and updating scroll positionInkberry
strange, for me vertical wasn't working so I made this change. BTW, how do you scroll horizontally using a mouse?Dauphin
E
1

SOLUTION, an conceptual extension to my answer to a similar question

CODE

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>

Just some things to clean up your code:

  • you're not using the object in an OO fashion, no need for new/use the function as a constructor (and if you are going to, start it with a capital)

  • in this day and age the compiler can do a lot of things for us, there's no reason to use names like 'i' or 'j' where 'index' is much more self explanatory, and the if statement was made for what you used the and operator for (!data && /*set data*/, use if (!data) /*set data*/)

  • don't employ anonymous, call-once functions where they're not required, they need to be recompiled each time the parent function runs

  • declare variables outside of loops

  • don't use random() (in this case) unless the code is working, makes it harder to see whats going on

  • personal preference send null to a function instead of undefined and don't name a function $matrix when matrix is just as apt use comments!

  • personal preference unless you know what you're doing, ++x is exactly what you need and x++ is a minor waste of resources

  • because you're using horizontal scroll, forget about hiding the scrollbar

  • start out simple and don't attempt to get the height, just force one. things like detecting height are niceties reserved for working solutions

  • always use proper indentation, even if the if statement can be written on one line. Doesn't matter about your personal preference because you're posting the code online expecting other people to help you

  • when you edit code make sure you update the semantics. E.g. the element called 'heightForcer' now manipulates the width aswell, obviate that by calling it 'areaForcer'

Emilieemiline answered 21/8, 2013 at 0:58 Comment(10)
Glad to hear an answer from you!Inkberry
who can turn down a complex problem? and with such a high bounty too! :P thanks for the heads up with that comment you placed on my last answerEmilieemiline
I am bit wondering, may I know what was the problem with my code and where you have corrected it, except those points I just wanted to know logical mismatch happened. Could you please help meInkberry
it could have been any number of things. looking at your original code you seem to have extended the functionality in much the same way I had (looking inside refreshWindow()). It may well have just come down to a HTML/CSS issue, where the elements themselves won't behave if not configured correctly. One thing I did notice was that you removed the delay from delayingHandler(), which definitely would have made it behave a little odd (but not to a non-working state)Emilieemiline
There is a huge gap observed at the end of vertical scroll area. And when using cellWidth how to employ variable cellWidths in row. For example we will have row headers at the left, If I want to fix two columns at the left and remaining cells will be changing. Exactly like freezed columns and freezed rows. While employing freezed panes I may have to give variables widths to fixed rows and actual data rows. How to proceed with that? Could you please help me to get an idea of it.Inkberry
if you write code that's easy to read (indentation, and code conventions) as well as use logic and names that make the code almost self explanatory; problems will be easier to uncover. Make good use of the developer tools within the browser too, that's what I did to get the above workingEmilieemiline
please see my last comment.Inkberry
yes I noticed that issue too, I'm at work though and can't spend more time here as is. This should definitely push you in the right direction. It's a fiddle, so you can fiddle with it as much as you want. If anything goes wrong, inspect elements with the browser's dev-tools and try to figure out what's wrong. Don't give up, this is where the majority of learning happensEmilieemiline
I am facing problems while implementing fixed headers and fixed columns with this approach!Inkberry
I don't understand what you mean. What problems, what are you referring to when you say "fixed ..." etc? You may have to post a fiddle, or better yet, a new question (about the specific problem, not what you want done). NB not only are comments not the best place to converse, but StackOverflow isn't the best place for people to ask for whole solutions. It's a QA site, not a programmer outsourcing agency.Emilieemiline

© 2022 - 2024 — McMap. All rights reserved.