up/down arrow key issue with typeahead control (angular bootstrap UI)

15,090

Solution 1

Here is the working plunker

In this I have override ui-bootstrap typeahead as suggested in the answer.

Following are the changes I need to make it work:

Added following line in typeaheadMatch directive (line - 335 of ui.bootstrap.typeahead.js file in the plunker)

element[0].focus();

Added shouldFocus directive (line - 314 -350)

.directive('shouldFocus', function(){
  return {
   restrict: 'A',
   link: function(scope,element,attrs){
     scope.$watch(attrs.shouldFocus,function(newVal,oldVal){
       element[0].scrollIntoView(false);
     });
   }
 };
})

and finally added directive in li (line - 372)

should-focus=\"isActive($index)\"

Solution 2

@user1690588 answer works great. Only problem is that when the last item in the list is active and you press the down key. the scrollToView tothe first item does not work. I added a check to ensure newVal is true which seems to fix this.

  if (newVal) { 
    element[0].scrollIntoView(false);
  }

http://plnkr.co/edit/05yoHSlhvX740tgiRRXm

Solution 3

I used another approach to solve this problem, as none of the solutions here satisfying. Mainly:

  • reaching the last item and pressing the DOWN key should bring the first item into view
  • hovering items with the cursor should not make the Typeahead popup scroll
  • using mouse scroll wheel should not trigger weird behaviors on the Typeahead popup
  • fix should avoid modifying AngularUI Bootstrap sources

Extending AngularUI Bootstrap directives and listening to only specific key events seems to provide a cleaner workaround as you can see here: http://plnkr.co/edit/QuZw7j?p=preview

angular.module('ui.bootstrap.demo')
    .directive('typeahead', function () {
        return {
            restrict: 'A',
            priority: 1000, // Let's ensure AngularUI Typeahead directive gets initialized first!
            link: function (scope, element, attrs) {
                // Bind keyboard events: arrows up(38) / down(40)
                element.bind('keydown', function (evt) {
                    if (evt.which === 38 || evt.which === 40) {
                        // Broadcast a possible change of the currently active option:
                        // (Note that we could pass the activeIdx value as event data but AngularUI Typeahead directive
                        //  has its own local scope which makes it hard to retrieve, see:
                        //  https://github.com/angular-ui/bootstrap/blob/7b7039b4d94074987fa405ee1174cfe7f561320e/src/typeahead/typeahead.js#L104)
                        scope.$broadcast('TypeaheadActiveChanged');
                    }
                });
            }
        };
    }).directive('typeaheadPopup', function () {
        return {
            restrict: 'EA',
            link: function (scope, element, attrs) {
                var unregisterFn = scope.$on('TypeaheadActiveChanged', function (event, data) {
                    if(scope.activeIdx !== -1) {
                        // Retrieve active Typeahead option:
                        var option = element.find('#' + attrs.id + '-option-' + scope.activeIdx);
                        if(option.length) {
                            // Make sure option is visible:
                            option[0].scrollIntoView(false);
                        }
                    }
                });

                // Ensure listener is unregistered when $destroy event is fired:
                scope.$on('$destroy', unregisterFn);
             }
        };
    });

Tested with AngularJS 1.4.5 & AngularUI Bootstrap 0.13.4, but this should work with other recent versions.

[Note that I had to manually include jQuery, as the one preloaded by Plunker (jQuery v1.8.3) will fail retrieving the current active option.]

Share:
15,090
ankitd
Author by

ankitd

going through learning curve of HTML5 ,CSS3 and angularJS.

Updated on June 05, 2022

Comments

  • ankitd
    ankitd about 2 years

    Check this PLNKR , i have implemented typeahead control, By default in type ahead control they are not setting any max-height or height to list, but as per requirement i have to fix height of list to 110px. So, when we have longer list at a time only 4 data will be shown and rest can be seen by scrolling down. Scrolling is working while user click on scroll up/down arrows, but it's not working with keyboard up/down keys.

    The problem is explained in steps:-

    1. Type something i.e "a" to get data in typeahead (list will be populated)
    2. Press down-arrow key (focus will be on list item)
    3. Press down-arrow key 4-5 time to go further down (When we traverse down to list, scroll is not getting moved.)
    4. It always show top 4 items in list. Ideal behavior is it should shift.

    User is able to scroll by clicking on scroll manually but with arrow key it's not scrolling.

    Type "a" and traverse down to list with down-arrow key scrolling is not working.

    HTML

    <!doctype html>
    <html ng-app="ui.bootstrap.demo">
    <head>
     <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
     <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.js"></script>
     <script src="example.js"></script>
     <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
     <link href="style.css" rel="stylesheet">
    </head>
    <body>
    <div class='container-fluid' ng-controller="TypeaheadCtrl">
      <h4>Static arrays</h4>
      <pre>Model: {{selected | json}}</pre>
      <input type="text" ng-model="selected" typeahead="state for state in states | filter:$viewValue | limitTo:8" class="form-control">
     </div>
    </body>
    </html>
    

    CSS

    .dropdown-menu{
     height:110px;
     overflow:auto;
    }
    

    javascript datalist

    $scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
    
  • KanhuP2012
    KanhuP2012 over 9 years
    This is not working when page having scroll and input element is little below.please help on this
  • MKB
    MKB over 9 years
    @KanhuP2012 - Can you please create a plunker that demonstrate this behavior? That will be very helpful to get the bug fix. Thanks
  • KanhuP2012
    KanhuP2012 over 9 years
    please find the plunk for this plnkr.co/edit/qpkrmBVFNZSpxpC4eeJt?p=preview on selection of dropdown main page view scrolls back.
  • MKB
    MKB over 9 years
    @KanhuP2012 - OK, You got the solution, cool. You should post that as an answer.
  • RushabhG
    RushabhG about 4 years
    @MKB Hi, I am facing this same issue but with Angular 8.2 and ngx-bootstrap 4.1.1. How do I resolve it there? Thanks
  • RushabhG
    RushabhG almost 4 years
    Hi, I am facing this same issue but with Angular 8.2 and ngx-bootstrap 4.1.1. How do I resolve it there? Thanks