NeXTs / Clusterize.js

Tiny vanilla JS plugin to display large data sets easily
https://clusterize.js.org
MIT License
7.22k stars 412 forks source link

Scroll to specific item in list #55

Closed OscarGodson closed 8 years ago

OscarGodson commented 9 years ago

I have a map up top and a table below. The table rows below link to a marker on the map. I want to make this bidirectional. So if you click on a marker it selects a in clusterize and marks it selected. The issue I'm running into is how do I scroll to elements that dont exist yet. Is there a trick to figuring out where a row will be so I know what to scrollTo()?

I haven't tried it yet but the only other idea I had was to remove height restriction, find the offset of the element, reapply the height, then scrollTo.

NeXTs commented 8 years ago

This is interesting question, Oscar.

Does your rows have same height?

OscarGodson commented 8 years ago

Its user set, so each cell/row could be totally different heights :\

NeXTs commented 8 years ago

For calculations clusterize keeps in memory the height of single row only and hopes that all other rows have roughly similar height. It does not stores the individual height of every row which means that position of line you are looking for could not be predicted in ease manner :\

I'll think about what else can be done here, and meanwhile the workaround which you proposed seems the only possible solution

OscarGodson commented 8 years ago

Is there a way to dynamically change the rows_in_block and stuff? I was thinking I could dynamically change that to be the length of the rows, call update() and do the scroll to hack I mentioned rather than changing the height of the element and everything. I didnt see anything on the docs.

NeXTs commented 8 years ago

Nope, rows_in_block cannot be changed dynamically, by design. But you are free to extend library as you wish.

At the moment, you can achieve the same result by:

Also take a look to playground's Destroy table section and click "Inserts all rows to the table" button which demonstrates such behaviour, then click "Init clusterize" button

OscarGodson commented 8 years ago

Sorry, figured it out.

OscarGodson commented 8 years ago
clusterize.destroy();
var $scrollToElement = $('[data-id="' + id + '"]');
var scrollToPosition = $scrollToElement.offset().top - $scrollToElement.parent().offset().top;

var clusterize = new Clusterize({...})
document.getElementById('scrollArea').scrollTop = scrollToPosition;

This almost works. The only issue is I think Clusterize is altering the height or something. If I scroll to an element within the available DOM the above works. If it's not in the DOM the scroll position is off. If I remove the reinit code it scrolls to the element perfectly.

I can kind of hack it by doing this code twice and in a timeout (timeout because sometimes its not rendered yet after enabling the clusterize again, maybe because of the debounce?). So after the scrollTop in my code example above I do this:

    setTimeout(function () {
      var $scrollToElement = $('[data-id="' + id + '"]');
      var scrollToPosition = $scrollToElement.offset().top - $scrollToElement.parent().offset().top;
      document.getElementById('scrollArea').scrollTop = scrollToPosition;
    }, 10)

That gets it almost perfect but this requires double height calculations and still not perfectly lined up. Also the code is super janky :P any other suggestions welcome.

My next attempt I'm going to scroll in chunks of 100px, check if the element exists, and keep scrolling until the element is found and see which is faster and more accurate.

OscarGodson commented 8 years ago

Dang, I'm just not getting it. I've tried every hacky way I can think of. My final theory was to do the scrolling but it doesn't work because sometimes when I do scrollTop it gets "stuck" with an empty row up top and thus causing an infinite loop since it can never scroll higher. Like these two are both times I tried to scroll up to 20:

map of top 100 websites worldwide 2015-11-18 13-55-23 map of top 100 websites worldwide 2015-11-18 13-57-11

And scrolling down never quite hits the mark. Again, tried to go to row 20.

map of top 100 websites worldwide 2015-11-18 13-57-37

I've tried different speeds (as fast as 10ms, and weird numbers like 997ms) and also tried offsetting the first/last element offsets by like 100px or other numbers. It always gets stuck or is off.

Here's a demo of the behavior: http://jsbin.com/qibihah/edit?css,js,output

Try scrolling to the bottom (item 99) then type in 40. For me I get stuck on item 81 and it never scrolls higher. From the top go to 50. 50 is scrolled past view and you can't see it. In the JSBin example I included a way to log out the events happening so you can see whats happening in the loop

NeXTs commented 8 years ago

@OscarGodson It's still little jumpy but as a concept should be fine :) http://codepen.io/NeXTs/pen/VvRyJE?editors=101

NeXTs commented 8 years ago

@OscarGodson So how it's going? This helped to solve your task?

OscarGodson commented 8 years ago

Unfortunately, no. I'm still not entirely sure how to fix it yet. Your example still breaks if you make the content more than 1 line each. Here's some examples:

99 goes to 137 a pen by denis 2015-12-02 15-25-24

125 went to 109/110 a pen by denis 2015-12-02 15-27-16

OscarGodson commented 8 years ago

It might be next to impossible actually. I noticed sometimes there's these giant jumps between blocks. Like in this table I'm at 21: map of top 100 websites worldwide 2015-12-02 16-07-16

Then I scroll up like 2px and... map of top 100 websites worldwide 2015-12-02 16-08-57

vamp commented 8 years ago

@OscarGodson, try to use clusterize.js properly (main requirement - same height of each row in table)

NeXTs commented 8 years ago

@OscarGodson Wow, those rows height vary too much. Margin of error in the row height may be about 50% - 150% while your rows height varies up to 400% times which leads to false miscalculations of overall cluster height.

Your example still breaks if you make the content more than 1 line each

I'll investigate it in my spare time

OscarGodson commented 8 years ago

I went ahead and added white-space: nowrap. It doesn't really make it readable for some longer lines but scrolling to a specific line works. Most content isn't longer than an address so it'll do for now. After forcing it to one line with that CSS my "scrollToRow" method is just this

$('#scrollArea').scrollTop($(this.container).find('[data-id]').height() * id);