简体   繁体   English

刷新后在 AngularJS 中保持滚动位置

[英]Persisting scroll position in AngularJS after refresh

I've got a web app I've written with AngularJS: http://asmor.com/anr我有一个用 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.当你将卡片添加到你的牌组时,它们会被添加到右上角的一个固定位置的 div 中。 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.该 div 具有动态设置的最大高度,因此它不会被浏览器窗口的底部或页面右下角的另一个固定 div 遮挡。

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.现在的问题是,当您通过单击红色或绿色按钮滚动时从卡组中添加或删除卡时,Angular 会重新绘制卡组列表并将滚动重置回顶部。

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.据我所知,Angular 在重绘页面时会多次重绘页面。 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.我不知道最后一次重新绘制是什么时候,而且我不能每次都盲目地设置 scrollPosition 因为如果有人自己滚动到顶部,我将无法分辨两者之间的区别如果它因为重绘而滚动到顶部。

One option I've considered is using a setTimeout to clear $scope.deckScrollPosition, so that我考虑过的一种选择是使用 setTimeout 来清除 $scope.deckScrollPosition,以便

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)我能够通过使用 setTimeout 清除滚动位置变量来使其工作(以便每次重绘都可以访问该变量)

$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. ...但这似乎真的很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 (eg maybe I've coded something poorly that's forcing it to redraw multiple times?)使 angular 每次数据更改仅重绘一次(例如,也许我编码的东西很差,迫使它重绘多次?)

  2. Somehow cope with the redraw without resorting to setTimeout chicanery?以某种方式处理重绘而不诉诸 setTimeout 诡计?

Found an answer here:在这里找到了答案:

http://www.benlesh.com/2013/02/angular-js-scrolling-to-element-by-id.html 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和示例 plunkr,有点定制在这里: http ://plnkr.co/edit/CTVgvEoY7CnLX38o70i4?p=preview

Basically, you set the location.hash and the scroll to it.基本上,您设置 location.hash 和滚动到它。 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. e:刚刚注意到它实际上将视口集中在最后一个项目上,这实际上是不可取的。

A couple of things that should help you.一些应该对您有所帮助的事情。 If the items in deck.cardsByType have an ID or some unique field, you can tell AngularJS to track the items and only redraw ones with a new ID, the other items get updated, which should stop the whole list being re-drawn:如果deck.cardsByType的项目有一个ID 或一些唯一的字段,你可以告诉AngularJS 跟踪这些项目并且只用新的ID 重绘那些项目,其他项目会更新,这应该会停止整个列表的重绘:

<li ng-repeat="(cardName, qty) in typeHash.cards track by id">

You also said "I don't know when the last re-draw is going to be" - you can add a directive for when an item is drawn:您还说“我不知道最后一次重新绘制是什么时候” - 您可以添加一个指示何时绘制项目:

app.directive( 'repeatLoad', function(){
    return {
        link: function( scope, element, attrs ) {
            scope.$eval( attrs['repeatLoad'] );
        }
    }
}); 

Then add the directive with a function in your controller to call然后在控制器中添加带有函数的指令以调用

<li 
  ng-repeat="(cardName, qty) in typeHash.cards track by id"
  repeat-load="loadedElement()"
>

And you could count them in your controller, and check when the final one has been added您可以在控制器中计算它们,并检查何时添加了最后一个

$scope.cnt_cards = 0;
$scope.loaded_cards = 0;

$scope.loadedElement = function() {
  $scope.loaded_cards++;

  if( $scope.cnt_cards == $scope.loaded_cards ) {
    console.log('All cards loaded');
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM