2016-07-29 15 views
2

私は、任意のコンピュータ上のブラウザが実現可能な数の要素で、非常に大きなHTMLファイルを動的に作成しています。ビューポートで要素を取得する最速の方法

次に、ユーザーがスクロールするときに、実際にビューポート内にある特定のタイプの要素(divと言う)にアクセスする必要があります。

ビューポートに表示される要素のリストを取得する方法を知っている唯一の方法は、すべての要素をループし、その境界が現在のビューポートと重なるかどうかを確認することです。この問題は、ドキュメント内に非常に多くの要素が存在し、このプロセスがブラウザをスクロールさせるのに十分迅速に完了できないことがあります。

ビューポート内のすべての要素を素早く取得する方法はありますか?

+0

はすべてリストビューのこれらの要素ですか? – Wolfgang

+0

この回答を参照してください - http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport – marmeladze

+0

あなたが生成している場合あなたがリスナーを持つことができるノードの大きなグループのための親ノードは、どの親がビューポートにあるかを追跡し(例えば、http://imakewebthings.com/waypoints/)、可視性チェックを実行する(リンクされた@marmeladzeのように)その親の子供たちに –

答えて

0

分割して、より小さいエリア(FNOブロック)の窓の領域を分割することができる

征服。ウェブサイトが主に垂直の場合、複数の列は必要ありません(ブロック幅はドキュメントの幅に等しい)。ページを移入した後、各ノードとその子孫が存在するブロックに追加します(複数のブロックに含めることができます)。スクロールイベントハンドラでは、可視ブロック内のノードをチェックするだけです。各ブロックの高さがビューポートの高さに等しい場合は、ブロックを2つまでチェックするだけで済みます。 とページがを次のように常にブロックを見つけ、どのように大きなどんなに:次の例では

var from = Math.trunc(viewport.y/blockHeight) ; 
var to = Math.trunc(viewport.y2/blockHeight) ; 

、私はdinamically 10,500ノードを作成し、その後、あなたはページをスクロールすることができ、可視のdivはなりますリストされる。別の方法でノードをフィルタリングする場合は、matchと呼ばれる関数を適応させることができます。 Chromeと200,000ノードのタブレットでこれをテストしました。ページが読み込まれた後、スクロールがスムーズでした。コンピュータ/ブラウザが処理できるノードよりも多くのノードを追加すると、パフォーマンスが低下する可能性があります。アルゴリズムは正常に動作します。同じテストマシンと1,000,000ノードで、スクロールはリアルタイムでは起こっていませんでしたが、それほど遅くはありませんでした。

ページの読み込みには時間がかかりますので、しばらくお待ちください。

var blocks = [] ; 
 
var blockHeight ; 
 
var blocksNumber ; 
 
function isVisible(element, vp) 
 
{ 
 
    /* This checks if the element is in the viewport area, you could also 
 
    * check the display and visibility of its style. 
 
    */ 
 
    var rect = element.getBoundingClientRect() ; 
 
    var x = rect.left ; 
 
    var x2 = x + element.offsetWidth ; 
 
    var y = rect.top ; 
 
    var y2 = y + element.offsetHeight ; 
 
    return !(x >= vp.w || y >= vp.h || x2 < 0 || y2 < 0) ; 
 
} 
 
function matches(element, vp) 
 
{ 
 
    /* You can filter the elements even further */ 
 
    return element && element.id && element.tagName === "DIV" && isVisible(element, vp) ; 
 
} 
 
function viewport() 
 
{ 
 
    this.x = window.pageXOffset ; 
 
    this.w = window.innerWidth ; 
 
    this.x2 = this.x + this.w - 1 ; 
 
    this.y = window.pageYOffset ; 
 
    this.h = window.innerHeight ; 
 
    this.y2 = this.y + this.h - 1 ; 
 
    return this ; 
 
} 
 
function addMatch(element, array) 
 
{ 
 
    /* An element may be in more than one block, so you need 
 
    * to check if it wasn't already added. 
 
    */ 
 
    for(var i = 0 ; i < array.length ; ++i) 
 
    { 
 
     if(array[i] === element) return ; 
 
    } 
 
    array.push(element) ; 
 
} 
 
function onWindowScroll() 
 
{ 
 
    var msg = document.getElementById("msg") ; 
 
    var str = "" ; 
 

 
    var vp = new viewport() ; 
 
    var from = Math.trunc(vp.y/blockHeight) ; 
 
    var to = Math.trunc(vp.y2/blockHeight) ; 
 
    str += "Nodes: " + document.body.childNodes.length 
 
     + ", blocks: " + blocks.length 
 
     + ", searching blocks " + from + "-" + to + "<br>" 
 
     + "Founded: " ; 
 
    var array = [] ; 
 
    for(var b = from ; b <= to ; ++b) 
 
    { 
 
     var block = blocks[b] ; 
 
     for(var i = 0 ; i < block.length ; ++i) 
 
     { 
 
      if(matches(block[i], vp)) 
 
      { 
 
       addMatch(block[i], array) ; 
 
      } 
 
     } 
 
    } 
 
    if(array.length) 
 
    { 
 
     for(var i = 0 ; i < array.length-1 ; ++i) 
 
     { 
 
      str += array[i].id + " " ; 
 
     } 
 
     str += array[array.length-1].id ; 
 
    } 
 
    else 
 
    { 
 
     str += "none" ; 
 
    } 
 
    msg.innerHTML = str ; 
 
} 
 
function onWindowLoad() 
 
{ 
 
    setTimeout(function() 
 
    { 
 
     var i = 0 ; 
 
     /* Lets populate the page */ 
 
     while(i < 10000) 
 
     { 
 
      var element = document.createElement("DIV") ; 
 
      element.className = "first" ; 
 
      element.innerHTML = i ; 
 
      element.id = i++ ; 
 
      document.body.appendChild(element) ; 
 
      element = document.createElement("SECOND") ; 
 
      element.className = "second" ; 
 
      element.innerHTML = i ; 
 
      element.id = i++ ; 
 
      document.body.appendChild(element) ; 
 
     } 
 
     /* Lets add random positioned elements */ 
 
     var i = 0 ; 
 
     while(i < 500) 
 
     { 
 
      var x = Math.floor(Math.random() * document.body.offsetWidth) ; 
 
      var y = Math.floor(Math.random() * document.body.offsetHeight) ; 
 
      if(Math.random() < 0.5) 
 
      { 
 
       var element = document.createElement("DIV") ; 
 
       element.className = "absolute-first" ; 
 
      } 
 
      else 
 
      { 
 
       var element = document.createElement("SECOND") ; 
 
       element.className = "absolute-second" ; 
 
      } 
 
      element.style.left = x + "px" ; 
 
      element.style.top = y + "px" ; 
 
      element.id = "r" + i++ ; 
 
      element.innerHTML = element.id ; 
 
      document.body.appendChild(element) ; 
 
     } 
 
     /* Now we create the blocks */ 
 
     var nodes = document.body.childNodes ; 
 
     blockHeight = window.innerHeight ; 
 
     blocksNumber = Math.ceil(document.body.offsetHeight/blockHeight) ; 
 
     for(var b = 0 ; b < blocksNumber ; ++b) 
 
     { 
 
      blocks[b] = new Array() ; 
 
     } 
 
     /* And we add all the nodes into they corresponding blocks */ 
 
     for(var i = 0 ; i < nodes.length ; ++i) 
 
     { 
 
      addElement(nodes[i]) ; 
 
     } 
 
     addEventListener("scroll", onWindowScroll, false) ; 
 
     onWindowScroll() ; // Initialize msg 
 
    }, 20) ; 
 
} 
 
function addElement(element) 
 
{ 
 
    /* This works fine if the rest of the nodes stayed in the 
 
    * same position with the same size when element was added. 
 
    */ 
 
    if(!element.getBoundingClientRect) return ; 
 
    var rect = element.getBoundingClientRect() ; 
 
    var y = rect.top + window.pageYOffset ; 
 
    var y2 = y + element.offsetHeight ; 
 
    var from = Math.trunc(y/blockHeight) ; 
 
    var to = Math.trunc(y2/blockHeight) ; 
 
    for(var b = from ; b <= to ; ++b) 
 
    { 
 
     blocks[b].push(element) ; 
 
    } 
 
    var nodes = element.childNodes ; 
 
    if(nodes) 
 
    { 
 
     for(var i = 0 ; i < nodes.length ; ++i) 
 
     { 
 
      addElement(nodes[i]) ; 
 
     } 
 
    } 
 
} 
 
function removeElement(element) 
 
{ 
 
    /* This works fine if the rest of the nodes stayed in the 
 
    * same position with the same size when element was added. 
 
    */ 
 
    if(!element.getBoundingClientRect) return ; 
 
    var rect = element.getBoundingClientRect() ; 
 
    var y = rect.top + window.pageYOffset ; 
 
    var y2 = y + element.offsetHeight ; 
 
    var from = Math.trunc(y/blockHeight) ; 
 
    var to = Math.trunc(y2/blockHeight) ; 
 
    for(var b = from ; b <= to ; ++b) 
 
    { 
 
     var i = blocks[b].indexOf(element) ; 
 
     if(i > -1) 
 
     { 
 
      blocks[b].splice(i, 1) ; 
 
     } 
 
    } 
 
} 
 
addEventListener("load", onWindowLoad, false) ;
body 
 
{ 
 
    margin: 0 auto ; 
 
    text-align: center ; 
 
    font-family: sans-serif ; 
 
} 
 
/* Filtered in elements are light green */ 
 
.first 
 
{ 
 
    height: 50px ; 
 
    line-height: 50px ; 
 
    background-color: #cfc ; 
 
} 
 
/* Filtered out elements are light red */ 
 
.second 
 
{ 
 
    display: block ; 
 
    height: 30px ; 
 
    line-height: 30px ; 
 
    background-color: #fcc ; 
 
    box-sizing: border-box ; 
 
} 
 
/* Filtered in elements are light green */ 
 
.absolute-first 
 
{ 
 
    background-color: #cfc ; 
 
} 
 
/* Filtered out elements are light red */ 
 
.absolute-second 
 
{ 
 
    background-color: #fcc ; 
 
} 
 
.absolute-first, .absolute-second 
 
{ 
 
    position: absolute ; 
 
    padding: 1pt 5pt 1pt 5pt ; 
 
} 
 
.first, .second, .absolute-first, .absolute-second 
 
{ 
 
    border: 1px solid #444 ; 
 
} 
 
#msg 
 
{ 
 
    position: fixed ; 
 
    z-index: 1 ; 
 
    top: 0 ; 
 
    left: 0 ; 
 
    width: 100% ; 
 
    min-height: 24pt ; 
 
    line-height: 24pt ; 
 
    border: 1px solid #000 ; 
 
    background-color: #ffd ; 
 
    box-sizing: border-box ; 
 
    font-size: 14pt ; 
 
    vertical-align: middle ; 
 
    text-align: left ; 
 
    padding-left: 3pt ; 
 
    opacity: 0.7 ; 
 
}
<div id="a"> 
 
    <div id="a1"> 
 
     <div id="a11" class="first">a11</div> 
 
     <div id="a12" class="first">a12</div> 
 
    </div> 
 
    <div id="a2" class="first">a2</div> 
 
</div> 
 
<msg id="msg">Loading page, please wait...</msg>

唯一の欠点は、あなたがそれに対応するブロック内のノードを維持しなければならないことです。ブロックは、新しいノードを追加するとき、既存のノードを削除するとき、またはノードが移動またはサイズ変更されたときに更新される必要があります。最初にページにデータを入力したと言っているので、これは問題ではありません。

固定配置ノードは考慮されません。サポートを追加するのは簡単ですが、例には何も役立たないものがあります。

関連する問題