Search code examples
javascripthtmlangularjsfocusangularjs-ng-options

Scroll to a select option using AngularJS (focus not working on Chrome, IE11)


I need to scroll to a select option after the user moves it up or down and the option moves past the viewport. Note this issue is only observed on Chrome and IE11. It works fine on Firefox.

I tried using focus() or click() on the element, however, this does not work in Chrome. Note that this issue is not observed in FireFox.

Here is the HTML:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>AngularJS Select</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.min.js"></script>
        <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
        <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.css">

        <script src="script.js"></script>
    </head>
    <body ng-app="demo">
        <h1>AngularJS Select focus</h1>
        <div ng-controller="mainCtrl">
            <div class="col-md-5 col-sm-5">
                <div class="row">
                    <select class="form-control" style="min-height: 200px;"
                            id="Items"
                            multiple ng-multiple="true"
                            ng-model="captureRemoved"
                            ng-options="item.Title for item in selectedItems"
                            ng-value="item">
                    </select>
                </div>
            </div>
            <div class="col-md-1 col-sm-1" style="padding-right: 5px; padding-left: 5px;">
                <div style="padding-top: 36.5px;">
                    <div class="pull-center">
                        <a type="button" ng-click="moveDown()"><span><i class="fa fa-chevron-circle-down fa-lg" style="padding-left:8px">Down</i></span></a>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>

Javascript:

var app = angular.module('demo', []);


app.controller("mainCtrl", function ($scope) {
    $scope.selectedItems = [];//new Array();
    $scope.captureRemoved = [];
    $scope.item = "";

    $scope.load = function () {

        for (var i = 0; i < 30; i++) {
            var lbl = "item" + i;
            var item = {Id: i, Title: lbl};
            $scope.selectedItems.push(item);
        }

        $scope.selectedItem = 29;
        var x = 0;

    };

    $scope.load();

    $scope.moveDown = function () {
            var Id = 0;
            var origin = 0;
            var destination = 0;

            if ($scope.captureRemoved.length == 1) {
                Id = $scope.captureRemoved[0].Id;
            }

            for (var i = 0; i < $scope.selectedItems.length; i++) {
                if ($scope.selectedItems[i].Id == Id) {
                    origin = i;
                    destination = i + 1; // increment position or move down
                    break;
                }
            }

            if (destination >= 0 && destination < $scope.selectedItems.length && $scope.selectedItems[destination] != null) {
                var temp = $scope.selectedItems[destination];
                $scope.selectedItems[destination] = $scope.selectedItems[origin];
                $scope.selectedItems[origin] = temp;

                var m = document.getElementById('Items').options[destination];
               //m.click(); //does not work
                m.focus(); //does not work
            }

    };
});

I also need to set the focus on the element in case it scrolls past the viewport. e.g. Select Item 9 and click on the "Down" button twice. Now Item 9 cannot be viewed.

Can someone help?


Solution

  • To scroll the contents of the <select> element, set the scrollTop property of that element:

    if (destination >= 0 && destination < $scope.selectedItems.length && $scope.selectedItems[destination] != null) {
        var temp = $scope.selectedItems[destination];
        $scope.selectedItems[destination] = $scope.selectedItems[origin];
        $scope.selectedItems[origin] = temp;
    
        var m = document.getElementById('Items').options[destination];
        //m.click(); //does not work
        //m.focus(); //does not work
    
        $timeout(function() {
          var p = m.parentElement;
          //GET offsetTop of <option> element
          var x = m.offsetTop;
          //SET scrollTop of <select> element
          p.scrollTop = x-100;
        })
    }
    

    The above example uses the offsetTop property of the <option> element and sets the scrollTop property of the parent <select> element. It places the <option> element 100 pixels below the top of the scroll window.

    The DEMO on PLNKR