2

I am trying to find a way of scrolling to a list item (or anything for that matter) in KnockoutJS.

All I have is an object/id from a list, to which I wish to scroll.

I have put-together a functioning (and simplified) version of what I am trying to achieve below.

Demo: http://jsfiddle.net/qczdvkat/

JavaScript:

function ViewModel()
{
    var self = this;

    self.items = ko.observableArray([]);

    for(i = 0 ;i < 150; i++)
    {
        self.items.push({id: i, title: 'Item No. ' + i});
    }

    self.scrollToItem = function() {
        console.log('Scrolling to item 125...');
        $('ul').scrollTop($('ul [data-id="' + 125 + '"]').position().top - $('ul li:first').position().top);
    };
}

$(function() {
    ko.applyBindings(new ViewModel());
});

HTML:

<ul data-bind="foreach: items">
    <li data-bind="text: title, attr: {'data-id': id}">
    </li>
</ul>
<button data-bind="click: scrollToItem">Scroll to Item No. 125</button>

CSS:

ul
{
    max-height: 150px;
    overflow-y: scroll;
    border: 1px solid #000;
}

While the above works, I am looking for a way that is cleaner/more KnockoutJS and which does not involve the jQuery/data-* attributes which I am employing - since it works against the MVVM principles.

I suppose the root of the question boils down to: how to I get a DOM-element from a specific object inside a rendered list/tree/etc.?

While I have found a few resources here on StackOverflow, none solved this particular issue from what I saw.

JDR
  • 1,094
  • 1
  • 11
  • 31
  • Thanks James, I did see that question before posting. However, the difference is that the above situation describes a binding where it is quite straightforward to get hold of the respective DOM-element upon creation. In my case, however, all I have is an observableArray and a specific item that I want to scroll to. Hope that makes sense! – JDR Jul 06 '15 at 12:09
  • Unless I misunderstood, of course. ;-) – JDR Jul 06 '15 at 12:10
  • 1
    [Here's a demo](http://jsfiddle.net/qczdvkat/1/) based on your original. May or may not help! – James Thorpe Jul 06 '15 at 12:14
  • Right, I stand corrected - I simply didn't understand the other question in that case. This works flawlessly, although I'd be fantastic to not have the new property - I'll take a look. Thanks a bunch! – JDR Jul 06 '15 at 12:17
  • That binding does seem to have some issues - once you've scrolled an element into view you need to set it to `false` again before you're able to scroll into view again. will need proper testing in your scenario – James Thorpe Jul 06 '15 at 12:18
  • Yes, and the scrollTop also needs to be contained within the parent element - currently, it scrolls all containing elements. Thanks! – JDR Jul 06 '15 at 12:21
  • 2
    Alternatively you can pass an expression to @JamesThorpe's scrollTo handler: http://jsfiddle.net/290ew0nr/1/. This way you won't need an extra observable per item. – sroes Jul 06 '15 at 12:25
  • @sroes Yes, that's nicer. Also allows for re-scrolling to the same item etc. Final note - it's not my handler, I got it from the post I linked to, so I can't take any credit :) – James Thorpe Jul 06 '15 at 12:27
  • @sroes This is amazeballs and does it to a T - exactly what I have been wanting to achieve. Thanks! – JDR Jul 06 '15 at 13:07

0 Answers0