刷新后在AngularJS中保持滚动位置 [英] Persisting scroll position in AngularJS after refresh

查看:86
本文介绍了刷新后在AngularJS中保持滚动位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用AngularJS编写的Web应用程序:

示例plunkr,这里有一些自定义: http://plnkr.co/edit/CTVgvEoY7CnLX38o70i4?p =预览

基本上,您可以设置location.hash并滚动到它.希望它对您有用,因为您的设置会稍微复杂一些.

e:刚刚注意到它实际上将视口聚焦在最后一个项目上,这实际上是不希望的.

I've got a web app I've written with AngularJS: http://asmor.com/anr

This app is for helping to build decks for a particular game.

When you add cards to your deck, they're added to a fixed-positioned div in the top right corner. That div has a dynamically-set max-height so that it won't be occluded by the bottom of the browser's window or by another fixed div in the bottom right corner of the page.

If you hit the max height, the list of cards in the deck scrolls. See below:

Now the problem is that when you add or remove a card from the deck while it's scrolled by clicking the red or green buttons, Angular redraws the deck list and resets the scroll back to the top.

For convenience, here's the code I'm using for the deck list:

<div id="deck" class="shown-{{ deck.notEmpty }} faction{{ deck.identity.faction }}">
    <div class="deckStat cardTotal">
        <strong class="valid-{{ deck.enoughCards }}">Cards:</strong> {{ deck.cardCount }} / {{ deck.minCards }}
    </div>
    <div class="deckStat agendaPointsTotal shown-{{ deck.isCorp }}">
        <strong class="valid-{{ deck.enoughAgendaPoints }}">Agenda points:</strong> {{ deck.totalPoints }} / {{ deck.minPoints }}
    </div>
    <div class="deckStat influencePointsTotal">
        <strong class="valid-{{ deck.withinInfluenceLimit }}">Influence points:</strong> {{ deck.influenceTotal }} / {{ deck.influenceAvailable }}
    </div>

    <div class="deckIdentity" ng-mouseover="setPreviewLink(deck.identity)">
        <strong>Identity:</strong>
        {{ deck.identity.name }}
    </div>

    <div id="deckScrollContainer" style="max-height: {{ getMaxDeckHeight() }}px">
        <ul ng-repeat="(typeName, typeHash) in deck.cardsByType">
            <li class="deckTypeHeader">
                <strong>{{ typeName }}</strong>
                <span class="quantity">
                    ({{ typeHash.qty }})
                </span>
            </li>
            <li ng-repeat="(cardName, qty) in typeHash.cards" class="card faction{{ getCardFaction(cardName) }}" ng-mouseover="setPreviewLink(cardName)">
                <button class="btn btn-mini btn-success add qty{{qty}}" ng-click="addCard(cardName)"><i class="icon-plus"></i></button>
                <button class="btn btn-mini btn-danger remove qty{{qty}}" ng-click="removeCard(cardName)"><i class="icon-minus"></i></button>
                <span class="quantity">
                    {{ qty }}x
                </span>
                {{ cardName }}
                <span class="influence">
                    {{ getInfluenceString(cardName, qty) }}
                </span>
            </li>
        </ul>
    </div>
</div>

One thing I've tried was adding a function when you add or remove cards that will grab the current scroll position, and then later on replace it.

$scope.addCard = function (c) {
    $scope.setDeckScroll();
    $scope.deck.add(c);
};
$scope.setDeckScroll = function () {
    $scope.deckScrollPosition = document.getElementById("deckScrollContainer").scrollTop;
};

And...

$scope.getCardFaction = function (card) {
    $scope._persist();
    card = getCardByName(card);
    return card.faction;
};
$scope._persist = function () {
    // Any weird stuff that needs to be done on redraw/refresh
    if ($scope.deckScrollPosition) {
        document.getElementById("deckScrollContainer").scrollTop = $scope.deckScrollPosition;
        $scope.deckScrollPosition = null;
    }
};

As far as I can tell, Angular is redrawing the page many times when it does redraw it. I don't know when the last re-draw is going to be, and I can't just blindly set the scrollPosition every time because then if someone scrolled to the top on their own I wouldn't be able to tell the difference between that and if it had scrolled to the top because of a redraw.

One option I've considered is using a setTimeout to clear $scope.deckScrollPosition, so that

I was able to get it to work by clearing the scroll position variable with a setTimeout (so that every redraw would have access to the variable)

$scope._persist = function () {
    // Any weird stuff that needs to be done on redraw/refresh
    if ($scope.deckScrollPosition) {
        document.getElementById("deckScrollContainer").scrollTop = $scope.deckScrollPosition;
        setTimeout(function () {
            $scope.deckScrollPosition = null;
        }, 100);
    }
};

...but this seems really hacky. I'm hoping there might be a better way. Any ideas on how I could either...

  1. Make angular only redraw once per change in the data (e.g. maybe I've coded something poorly that's forcing it to redraw multiple times?)

  2. Somehow cope with the redraw without resorting to setTimeout chicanery?

解决方案

Found an answer here:

http://www.benlesh.com/2013/02/angular-js-scrolling-to-element-by-id.html

And the example plunkr, a little customised is here:http://plnkr.co/edit/CTVgvEoY7CnLX38o70i4?p=preview

Basically, you set the location.hash and the scroll to it. Hope it works for you, as you setup is a little more complex.

e: Just noticed that it actually focusses the viewport on the last item, which is in fact undesireable.

这篇关于刷新后在AngularJS中保持滚动位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆