2

JavaScript でページネーション システムを作成しようとしています。

基本的な状況: かなり長いテキスト (ストーリーの章、5000 語以上) を保持するデータベースがあります。これらの章を Web サイトに表示したいのですが、テキスト全体を一度に表示するのではなく、読みやすさをかなり損なうため、ページ単位で表示します。テキストの表示に問題はありませんが、ページを正しく表示することに問題はありません。

私は周りを見回していて、私がやりたいことをするJQueryコードに出くわしました...しかし、この方法には大きな注意点があります。テキストのページ付けが完了するまでに約 10 秒かかりますが、これは非常に長い待ち時間です。

コードの基本的な動作: テキストを単語に分割します (スペースで区切られます)。次に、単語を次々と innerHTML に追加しようとし、テキストが収まるはずのコンテナーよりも大きくなっているかどうかを確認します。

境界を破るたびに、前の文字列に戻り、新しいページを作成します。(テキストをスパンにカプセル化することにより、すぐに非表示/表示できます)これは機能しますが、これらのチェックを5000回以上実行する必要があるため、遅すぎます。

基本的に単語の量を取り、係数0.5で割り、バッファが必要なサイズよりも大きいかどうかを確認し、バッファが必要なサイズよりも「小さくなる」までこのプロセスを繰り返す近似システムを作成しようとしました初めて、その位置から、いっぱいになるまでバッファーをいっぱいにします。

ただし、正しく機能していないようです (ダブルワード、行、完全にいっぱいではなく、それでも遅すぎます)。

これは私が現在使用しているコードです。より簡単にするための修正と提案に感謝します。特に:より速く。ああ、いいえ、サーバー側でページングすることはオプションではありません。さまざまなブラウザー形式に適合するはずなので... 1280x768 解像度のフルスクリーンブラウザーでは、1024x768 解像度の小さなブラウザーよりもページ数が少なくなります。

function CreateChild(contentBox, Len, pageText, words) {
    var Child = document.createElement("span");
Child.innerHTML = pageText;
contentBox.appendChild(Child);
if(Len == 0) ++Len;
words.splice(0, Len);
    return words.length;
}

$(document).ready(function(){  
    var src = document.getElementById('Source');
    var contentBox = document.getElementById('content');
var inner = document.getElementById('inner');
    //get the text as an array of word-like things
    var words = src.innerHTML.replace(/ +/g, " ").split(' '), wCount = words.length;

    //start off with no page text
    var pageText = null, cHeight = contentBox.offsetHeight;

    while(words.length > 0) {
        var Found = false;
        pageText = words[0];    //Prevents constant checking for empty
        wCount *= 0.5;      //Searches, until the words fit in.
        for(var i = 1; i < wCount; ++i) pageText += ' ' + words[i];
        inner.innerHTML = pageText;
        Distance = inner.offsetHeight - cHeight;
        if(Distance < 40) {         //Less than two lines
            wCount = Math.floor(wCount);
            if(Distance < 0) {      //Already shorter than required. Fill.
                for(var i = wCount; i < words.length; ++i) {
                    //add the next word to the pageText
                    var betterPageText = pageText + ' ' + words[i];
                    inner.innerHTML = betterPageText;
                    //Checks, whether the new words makes the buffer too big.
                    if(inner.offsetHeight > cHeight) {
                        wCount = CreateChild(contentBox, i, pageText, words);
                        Found = true;
                        break;
                    } else {
                        //this longer text still fits
                        pageText = betterPageText;             
        }
        }
    } else {
        for(var i = wCount; i >= 0; --i) {
            //Removes the last word from the text
        var betterPageText = pageText.slice(0, pageText.length - words[i].length - 1);
        inner.innerHTML = betterPageText;

        //Is the text now short enough?
        if(inner.offsetHeight <= cHeight) {
            wCount = CreateChild(contentBox, i, pageText, words);
            Found = true;
            break;
        } else {
            pageText = betterPageText;             
        }
        }   
    }
    if(!Found) CreateChild(contentBox, i, pageText, words);
    }
}

//Creates the final block with the remaining text.  
Child = document.createElement("span");
Child.innerHTML = pageText;
contentBox.appendChild(Child);

//Removes the source and the temporary buffer, only the result remains.     
contentBox.removeChild(inner);
src.parentNode.removeChild(src);

    //The rest is the actual pagination code, but not the issue
});
4

3 に答える 3

5

リッチの提案のおかげで、問題を解決することができました。私がやっていること: まず、「ソース」からテキストを取得しています (または、テキスト全体を直接 JS に書き込むこともできます。効果は同じです)。

次に、一時バッファーからターゲットへの参照を取得します。一時バッファーはターゲット バッファー内にあるため、幅情報が保持されます。

その後、テキスト全体を単語に分割します (複数のスペースを 1 つのスペースに置き換えた後、標準の正規表現)。この後、関数の結果をバッファリングするためのいくつかの変数を作成します。これにより、関数呼び出しを不必要に繰り返す必要がなくなります。

ここでの主な違い: 20 単語のチャンクを取得し、現在のチャンクが境界を超えているかどうかを確認します (繰り返しますが、結果を変数にバッファリングして、複数回呼び出されないようにします。関数呼び出しは貴重なマイクロ秒に相当します)。

境界を超える (または文字数の合計に達する) と、ループが停止し、(境界が「停止」の原因であると仮定して) テキストが再び収まるまで、実行ごとに 1 単語ずつテキストが短縮されます。

最後に、新しいテキストが新しいスパン要素に追加され、コンテンツ ボックスに追加されます (ただし、非表示になります。理由は後で説明します)。「使用した」単語が単語配列から削除され、 wCount 変数は単語数だけ減分されます。

すべてのページがレンダリングされるまで、すすぎと繰り返しを繰り返します。「20」を他の値と交換できます。スクリプトは任意の数値で動作しますが、数値が小さすぎると「追加セグメント」で多くの実行が発生し、数値が大きすぎると「バックトラックセグメント」で多くの実行。

非表示について: スパンが表示されたままになっていると、遅かれ早かれスクロールバーが表示され、ブラウザ ウィンドウの幅が事実上狭くなります。これにより、収まる単語が少なくなり、後続のすべてのページがゆがんでしまいます (スクロールバーのあるウィンドウに一致するのに対し、「ページ化された結果」にはスクロールバーがないため)。

以下は私が使用したコードです。将来誰かに役立つことを願っています。

var src = document.getElementById('Source');
var contentBox = document.getElementById('content');
var inner = document.getElementById('inner');
//get the text as an array of word-like things
var words = src.innerHTML.replace(/ +/g, " ").split(' ');

//start off with no page text
var cHeight = contentBox.offsetHeight, wCount = words.length;

while(wCount > 0) {
    var Len = 1, Overflow = false;
    var pageText = words[0];                        //Prevents the continued check on 'is pageText set'.
    while(!Overflow && Len < wCount) {              //Adds to the text, until the boundary is breached.
        //20 words per run, but never more than the total amount of words.
        for(var j = 0; j < 20 && Len < wCount; ++Len, ++j) pageText += ' ' + words[Len];
        inner.innerHTML = pageText;
        Overflow = (inner.offsetHeight > cHeight);  //Determines, whether the boundary has been crossed.
    }
    if(Overflow) {                                  //Will only be executed, if the boundary has been broken.
        for(--Len; Len >= 0; --Len) {               //Removes the last word of the text, until it fits again.
            var pageText = pageText.slice(0, -(words[Len].length + 1)); //Shortens the text in question.
            inner.innerHTML = pageText;

            //Checks, whether the text still is too long.
            if(inner.offsetHeight <= cHeight) break;//Breaks the loop
        }
    }
    var Child = document.createElement("span");
    Child.style.display = "none";                   //Prevents the sidebars from showing (and distorting the following pages)
            Child.innerHTML = pageText;
            contentBox.appendChild(Child);
            words.splice(0, Len);
            wCount -= Len;
}   
于 2012-09-15T12:16:33.037 に答える
1

1ページの幅である絶対位置のコンテナを作成します。'auto'の高さを指定します。コンテナを画面の外のどこかに配置します。たとえば、左のように-10000pxにして、ユーザーに表示されないようにします。元のテキストを20語のチャンクに分割します。(これを実現する正規表現を検索します。)コンテナーの高さが単一ページの最大高さに達するまで、コンテナー内の文字列に一度に1つのチャンクを追加します。最大の高さに達すると、コンテナ内の文字列は基本的に1ページのテキストになります。コンテナ内の文字列を「pages」という配列にプッシュします。コンテナを空にして、20ワードのチャンクを再度追加し、前のページで中断したところから配列を繰り返し処理して、ページ2の作成を開始します。20ワードの配列の最後に到達するまで、このプロセスを続けます。コンテナの文字列が最大の高さに達するたびに、新しい各ページをページの配列にプッシュします。これで、ページの配列が作成され、各アイテムに各ページのテキストが含まれているはずです。

于 2012-09-15T08:37:45.590 に答える