【问题标题】:Persisting scroll position in AngularJS after refresh刷新后在AngularJS中保持滚动位置
【发布时间】:2013-05-03 08:23:36
【问题描述】:

我有一个使用 AngularJS 编写的网络应用程序:http://asmor.com/anr

此应用程序用于帮助为特定游戏构建套牌。

当您将卡片添加到牌组时,它们会添加到右上角的固定位置 div。该 div 具有动态设置的最大高度,因此它不会被浏览器窗口的底部或页面右下角的另一个固定 div 遮挡。

如果您达到最大高度,套牌中的卡牌列表会滚动。见下文:

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

为方便起见,这是我用于牌组列表的代码:

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

我尝试过的一件事是在您添加或删除将抓住当前滚动位置的卡片时添加一个函数,然后再替换它。

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

还有……

$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;
    }
};

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

我考虑过的一个选项是使用 setTimeout 来清除 $scope.deckScrollPosition,这样

我能够通过使用 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);
    }
};

...但这似乎真的很hacky。我希望可能有更好的方法。任何关于我怎么做的想法......

  1. 每次更改数据时只重绘一次 Angular(例如,也许我编写的代码很糟糕,迫使它重绘多次?)

  2. 在不使用 setTimeout 诡计的情况下以某种方式应对重绘?

【问题讨论】:

    标签: javascript angularjs scroll


    【解决方案1】:

    在这里找到答案:

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

    还有plunkr的例子,这里有点定制:http://plnkr.co/edit/CTVgvEoY7CnLX38o70i4?p=preview

    基本上,您设置 location.hash 和滚动到它。希望它对您有用,因为您的设置有点复杂。

    e:刚刚注意到它实际上将视口聚焦在最后一项上,这实际上是不可取的。

    【讨论】:

    • 我不认为这真的适用于我的情况。首先,我不想滚动到特定元素,我只是想防止 div 在重绘时跳来跳去。其次,我不清楚这是否适用于滚动 div 内的某些东西;似乎是用于滚动文档。第三,这似乎也不能帮助我解决多次重绘问题。
    • 你有没有看过 plunker?它完全符合您的要求,即使它可能不是确切的技术解决方案。我更改了 CSS 以反映您页面上的内容。如果视图被重绘太多或不是特定于您的代码,但通常是角度的摘要周期。如果您认为是这种情况,您应该提出一个新问题。
    【解决方案2】:

    有几件事应该对您有所帮助。如果deck.cardsByType 中的项目有一个 ID 或一些唯一字段,您可以告诉 AngularJS 跟踪这些项目并只用新 ID 重绘项目,其他项目会更新,这应该会停止重新绘制整个列表:

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

    您还说过“我不知道最后一次重绘是什么时候”——您可以添加一个指令来说明何时绘制项目:

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

    然后在你的控制器中添加带有函数的指令来调用

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

    您可以在控制器中对它们进行计数,并检查何时添加了最后一个

    $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');
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-21
      • 1970-01-01
      • 2011-07-31
      • 1970-01-01
      • 2020-11-28
      • 2013-07-12
      • 2020-02-29
      • 2021-07-07
      相关资源
      最近更新 更多