12

I'm trying to use $anchorScroll to scroll down the page to ensure that a row of a table is shown. I've read most of the SO threads and docs about $anchorScroll. I'm using it correctly, as far as I can tell. I've stepped through the code with Firebug, and it appears that the element id I'm using is correct.

When I execute the function that should change the scroll location, it does change the scroll location, but it just scrolls up, going all the way to the top. My "target" element that I want to scroll to is further down the page from where I execute the function.

There are no error messages, it just doesn't do what I need.

Here is the simple function that I use:

$scope.scrollTo = function (elementId) {
        console.log("element[" + angular.element(elementId) + "]");
        $location.hash(elementId);
        $timeout(function() {
            $anchorScroll();
        });
    };

I've also tried changing the reference to this so that instead of targetting a table row, it targets the accordion div that encloses the table, but that made no difference. It still just jumps to the top of the page.

Note that before I call "scrollTo", I first ensure that the accordion with the table is opened. In any case, it still doesn't scroll correctly even after it's manually opened.

Update:

Here's a portion of the HTML that I'm trying to scroll to:

                <div ng-controller="WorkflowDefsCtrl">
                <pane accordion-group heading="Workflows" is-open="accordionActiveFlags.workflowDefs" id="workflowDefs">
                    <label for="workflowDefsTable">Workflow Definitions</label>
                    <table id="workflowDefsTable" ng-table class="table">
                        <tr ng-repeat="workflowDef in sunlightConfig.workflowDefinitions | orderBy: workflowDef.order" id="workflowDef{{workflowDef.id}}">
                            <td data-title="'ID'">{{workflowDef.id}}</td>
                            <td data-title="'Name'">{{workflowDef.name}}</td>
                            <td data-title="'Label'">{{workflowDef.label}}</td>
                            <td data-title="'Order'" class="text-right">{{workflowDef.order}}</td>
                            <td data-title="'Render?'">{{workflowDef.render}}</td>
                            <td data-title="'Query Fragment'">{{workflowDef.queryFragment}}</td>
                            <td data-title="'Query Order'" class="text-right">{{workflowDef.queryOrder}}</td>
                        </tr>
                    </table>
                </pane>
            </div>

The two testcases I'm trying are going to element "workflowDefs" and any of the "workflowDef{{workflowDef.id}}" elements.

Update:

I enhanced my "scrollTo" method to deal with scrolling to elements that had just become visible. This doesn't make any difference, however. It still just scrolls to the top no matter what.

Update:

Today I realized that a string argument to "angular.element" should be a CSS selector, not an element id, so I had to add "#" as a prefix. This resulted in the correct element being located, but unfortunately it still had no effect on the display. It still doesn't scroll to the element.

My new "scrollTo" function looks like this:

    scrollTo:   function (elementId) {
        $location.hash("#" + elementId);
        $timeout(function() {
            $anchorScroll();
        });
    },
David M. Karr
  • 14,317
  • 20
  • 94
  • 199

2 Answers2

19

I managed to figure this out. I correctly assumed that $anchorScroll has to be executed in the $timeout block, but that isn't sufficient. The call to $location.hash() also has to be in the $timeout block. Once I changed my scrollTo function to the following:

    scrollTo:   function (elementId) {
        $timeout(function() {
            $location.hash(elementId);
            $anchorScroll();
        });
    },

It then started working consistently.

Drewness
  • 5,004
  • 4
  • 32
  • 50
David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • 1
    If you put in some small time like 100 ms for the $timeout it works great for me. I also didn't need the $location.hash() as per the documentation you can just put the element id in as an argument for $anchorScroll(). So: $timeout(function() { $anchorScroll("some-id"); }, 100); – user5489654 Nov 18 '17 at 16:49
0

When the state is changed & $location.hash() is not empty, then scroll to the desired hash id element, within the navigated page. $timeout would ensure that scrolling to the hash id block, is done after the page is loaded properly.

$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
    if ($location.hash()) {
        $timeout(function() {
            var hashId = $location.hash();
            $anchorScroll();
            document.getElementById(hashId).focus();
        });
    }
 });
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358