twitter / typeahead.js

typeahead.js is a fast and fully-featured autocomplete library
http://twitter.github.io/typeahead.js/
MIT License
16.52k stars 3.21k forks source link

Moving through suggestions doesnt move the scroll to always show the current element #914

Open jhuesos opened 10 years ago

jhuesos commented 10 years ago

When you have one more data sets with a max-height set and overflow-y:auto, navigating using keyboard doesnt move the scroll and that causes problems because maybe the current selected item is not visible.

I created a plunkr to show the issue:

http://plnkr.co/edit/Xj3wpn?p=preview

Type 'b' in the input and you will see that both data sets shows a scroll. Then try to navigate using keyboard.

We managed to add a quick workaround for that issue that seems to work fine in all situations but it is not clean (we added quickly to fix the issue).

element.on('typeahead:cursorchanged', function (event, selection, dataSetName) {
    var dropdownContainer, dataSetContainer, currentSelectedItem;
    var topPositionOfCurrentSelectedItem, currentScrollTopValue;
    var newScrollTopValue, maxHeightOfTheDataSetContainer, heightOfSuggestion;

    dropdownContainer = element.siblings('.tt-dropdown-menu');
    dataSetContainer = dropdownContainer.find('.tt-dataset-' + dataSetName);

    // Calculate the max height of current data set container
    maxHeightOfTheDataSetContainer = dataSetContainer.height();

    currentSelectedItem = dataSetContainer.find('.tt-cursor');
    topPositionOfCurrentSelectedItem = currentSelectedItem.position().top;
    currentScrollTopValue = dataSetContainer.find('.tt-suggestions')[0].scrollTop;

    topPositionOfCurrentSelectedItem = topPositionOfCurrentSelectedItem - dataSetContainer.position().top;

    // Item is out of view from the bottom
    if (topPositionOfCurrentSelectedItem >= maxHeightOfTheDataSetContainer) {
        heightOfSuggestion = currentSelectedItem.outerHeight();
        newScrollTopValue = currentScrollTopValue + heightOfSuggestion + (topPositionOfCurrentSelectedItem - maxHeightOfTheDataSetContainer);

        dataSetContainer.find('.tt-suggestions')[0].scrollTop = newScrollTopValue;
    }

    // Item is out of view from the top
    if (topPositionOfCurrentSelectedItem < 0) {
        newScrollTopValue =  currentScrollTopValue + topPositionOfCurrentSelectedItem;

        dataSetContainer.find('.tt-suggestions')[0].scrollTop = newScrollTopValue;
    }

    // If we reach here means the current item is visible and we dont need to do anything
});

Again this code might be for sure improved but seems to work fine for us. However, it would be of course better that this is somehow handle by typeahead internally.

UPDATED: I had to fix the demo because with the latest version of typeahead there were a couple of layout changes and that was the reason the styles were broken

sealemar commented 10 years ago

I'm not sure what's going on in your example, but if I type "d", then first three items in the list stay static, while last three can be scrolled over. As for overflows take a look at http://css-tricks.com/popping-hidden-overflow/ as those are quiet tricky

jhuesos commented 10 years ago

In order to see the scrollbar, you have to have more than 3 results return in the data set.

Let me explain a bit more about the example: In that page I am using 2 data sets: one for NHL teams and one for NBA teams. When you type something you are searching against those two datasets. Every dataset container has a max height of 120 pixels, so basically if you have 3 or less results in one data set, you will no see the scroll bar, if you have more than that, because the height of the data-set container will be more than 120px, that is when we show the scrollbar.

I improved my example to make it more clear the differences between both datasets:

http://plnkr.co/edit/daRr2O6ApBdE8jhkf1nr?p=preview

So answering your questions, when you typed 'd', because there were only 3 results in the nba teams data set, thats why you dont see the scrollbar.

My code in the first example what it does is to calculate the position of the selected result in the data set and calculate if the scroll needs to be moved up or down in order to display it.

sealemar commented 10 years ago

I see. My suggestion is no better than what you proposed at the top. You might want to take a look at event_emitter.js:onSync() and typeahead.js onSync events

jharding commented 10 years ago

Thanks for bringing this to my attention, this is an edge case I haven't though of before.

bronson commented 9 years ago

Is this still an issue? I go to that Plunkr and I don't see any scroll in the completions menu. If I shrink the window, the page scrolls, but that seems like the expected behavior...?

jhuesos commented 9 years ago

For some reason, the demo is broken. I will fix the styles and see if that is fixed.

jhuesos commented 9 years ago

I fixed the demos now they should all be working.

Also I was trying to fix my workaround for this issue but I realized that now I can't. The problem is on 'typeahead:cursorchanged', now it is not longer being called with the name of the current data-set. For my fix to work I need to know in which data-set the cursor is in.

Was this a change intentional? Or is it a bug?

geigel commented 8 years ago

Does anyone have any updates on this? I just ran into this issue as well. If I have max-height and overflow set on my .tt-menu. Yet when I scroll to the bottom of my list with the DOWN arrow key and then attempt to scroll back up using the UP arrow key it doesn't move the selected element into view. Oddly, the example at http://twitter.github.io/typeahead.js/examples/ for scrollable menu works just fine.