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

我是研究僧i 提交于 2019-11-27 08:45:37
MKB

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)\"

@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

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.]

The accepted solution will fail if the main window is already scrolling. @user1690588 was on the right track.

Edit the ui-bootstrap-tpls-0.13.3.js and drop this in around line 5205 above .filter('typeaheadHighlight') at least until the AngularUI team decides to fix this. As well as applying the other change to the template that the accepted solution references.

.directive('shouldFocus', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.$watch(attrs.shouldFocus, function (newVal, oldVal) {
                if (newVal && element.prop("class").indexOf("active")) {
                    var par = element.parent("ul");
                    var scrollingTo = element.offset().top - par.offset().top + par.scrollTop();
                    // uncomment below section if you want the selected content to be 
                    // viewed at half the box height
                    // scrollingTo = scrollingTo - (par.height() / 2); 
                    par.scrollTop(scrollingTo);
                }
            });
        }
    }
})

Also if you want to get rid of the mouse artifact where it starts scrolling up until it hits the first entry or down till hits the last entry then remove from the template the ng-mouseenter attribute.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!