無限スクロールを実現する汎用的なjQuery

## 何がしたい?
タイトルそのままです。
**簡単にどこでも自由に無限スクロールしたい。**

｢そんなのプラグインに頼れば良いじゃ無いか｣と思われるかと思います。しかし、プラグインですとプラグインに合わせなければいけない部分も多く、【汎用的】とは言いがたいです。
単純にjavascriptを使用して、希望の位置までスクロールしたらajaxをコールしてデータを展開したいだけなのです。

**たったそれだけが全然上手くいかない!!**

そこで禁じ手とは知りつつこちらで質問の記事を投稿しました。
以下が最初の投稿です。

## 最初の投稿 (原文まま)
制作中のシステムに無限スクロールを組み込みたいと思います。
jQueryを使って下部5%の所までスクロールしたら次のコンテンツをajaxで読み込んでおります。
まずは以下のようなjavascriptを書いたのですが、**何度も重複読み込み**をしてしまいます。
いったい何がいけないのかさっぱりお手上げになってしまいました。

codeigniter3で自作のシステムです。
テンプレートエンジンはtwigを使用しています。
jQueryはv2.2.3です。
Bootstrapはv3.3.7です。

恥ずかしながら質問させて頂きます。
**このコードのどこがいけないのでしょうか?**

```blog.html
<script>
$(window).on('load scroll', function () {
    var scrollHeight= $(document).height(),
        scrollPosition = $(window).height() + $(window).scrollTop(),
        scrollRatio = (scrollHeight - scrollPosition) / scrollHeight;
    if (scrollRatio <= 0.05) {
        $.ajax({
            type: 'GET',
            scriptCharset: 'UTF-8',
            url: '../blog_add40',
            dataType: 'json'
        }).done(function (data) {
            var dataArray = data;
            $.each(dataArray, function (i) {
                if (dataArray[i].file_name != null) {
                    var img = '<img src="../visual/' + dataArray[i].file_name + '" class="img-responsive">';
                } else {
                    var img = '<img src="../visual/no_image.jpg" class="img-responsive">';
                }
                $('#blog_entry').append('<article">' +
                    '<h2>' + dataArray[i].title + '</h2>' +
                    img +
                    '<p>' + dataArray[i].release_date + '</p>' +
                    '</article>');
            });
        }).fail(function () {
            return false;
        });
    }
});
</script>
```

システムからjsonはちゃんと帰ってきて表示はされます。しかし、重複して送信されているらしく下部の発火点まで来ると8～30回以上バタバタバタと連続で表示が進んでしまいます。毎回ランダムな回数読み込んで止まります。再度発火点を表示させるとさらにドドーーっと読み込みが発生してしまいます。いろんなパターンを試してもう数日ハマっておりどうも上手くいきません。

どなたかご教授頂けませんでしょうか?

無限スクロールにするにはシステムにデータの開始位置も投げなきゃならないのに、変なところで躓いております。

よろしくお願いいたします。

## コメントを頂いた＼(^O^)／
こういった所へ書き込みをするのも初めてなら、コメントを頂くことも初めてです。

**とっても嬉しい**

ありがとうございます。

## ヒントがあった
頂いたコメントの中に重要なヒントがありました。
> scrollイベントはスクロールする度に呼ばれます。
このため scrollRatio <= 0.05 の領域でスクロール中に何度もajaxコールが呼ばれてしまっているはずです。

そっか!!
発火点を作ったつもりが発火帯を作ってしまったんですね。
発火点に入ったら1回トリガーされるつもりが、発火帯に入ったピクセル数分トリガーされていたんですね。しかも割合で指定しているものだから、ドキュメントのサイズが大きくなればなるほど、トリガー地帯も広がっていたのですね。
データの読み込み中はトリガーされないようにもしたのですが、おそらく停止と解除が交互にトリガーされていたと想像します。無限スクロールですので、1回読み込んだら終了ではありません。指定位置が表示されたら何度でもデータが無くなるか指定した回数まで読み込みたいのです。

## 変えてみたけど･･･
```blog.html
<script>
    $(window).on('load scroll', function () {
        var documentHeight= $(document).height(),
            scrollBottomPosition = $(window).height() + $(window).scrollTop(),
            triggerPoint = documentHeight - scrollBottomPosition;
        if (triggerPoint <= 2) {
            $.ajax({
                type: 'GET',
                scriptCharset: 'UTF-8',
                url: '../blog_add40',
                dataType: 'json'
            }).done(function (data) {
                var dataArray = data;
                $.each(dataArray, function (i) {
                    if (dataArray[i].file_name != null) {
                        var img = '<img src="../visual/' + dataArray[i].file_name + '" class="img-responsive">';
                    } else {
                        var img = '<img src="../visual/no_image.jpg" class="img-responsive">';
                    }
                    $('#blog_entry').append('<article>' +
                        '<h2>' + dataArray[i].title + '</h2>' +
                        img +
                        '<p>' + dataArray[i].release_date + '</p>' +
                        '</article>');
                });
            }).fail(function () {
                return false;
            });
        }
    });
</script>
```

ちょっと分かりにくいので名前も変えました。
えーーと、｢triggerPoint <= 2｣となっております。
｢発火点じゃなくて、また発火帯になっているじゃ無いか!!」
はい、その通りです。
｢triggerPoint === 2｣とかにするとマウスの使い方やフリックの仕方で反応したりしなかったり。範囲指定でもダメでした。ちょっと上にしたり0にしたり。全滅でございます。Microsoft EdgeとAndroid6のChromeのみのテストですがこの2つでダメだったので他のデバイスではテストしていません。

## ヨシッ!! 分かった!! 【解決編】
```blog.html
<script>
    let triggerFlag = false;
    $(window).on('load scroll', function () {
        var documentHeight= $(document).height(),
            scrollBottomPosition = $(window).height() + $(window).scrollTop(),
            triggerPoint = documentHeight - scrollBottomPosition;
        if (!triggerFlag && triggerPoint <= 50) {
            triggerFlag = true;
            $.ajax({
                type: 'GET',
                scriptCharset: 'UTF-8',
                url: '../blog_add40',
                dataType: 'json'
            }).done(function (data) {
                var dataArray = data;
                $.each(dataArray, function (i) {
                    if (dataArray[i].file_name != null) {
                        var img = '<img src="../visual/' + dataArray[i].file_name + '" class="img-responsive">';
                    } else {
                        var img = '<img src="../visual/no_image.jpg" class="img-responsive">';
                    }
                    $('#blog_entry').append('<article>' +
                        '<h2>' + dataArray[i].title + '</h2>' +
                        img +
                        '<p>' + dataArray[i].release_date + '</p>' +
                        '</article>');
                });
            }).fail(function () {
                return false;
            });
        }
        if (triggerPoint >= 51) {
            triggerFlag = false;
        }
    });
</script>
```

イロイロ試した結果、"scroll"は使い方次第で結構アバウトっぽい。
ですので、確実に発火させるためにはやっぱり発火地帯を作るしか無いみたいです。しかし、発火地帯にしてしまうと、何度も意図しない発火を引き起こします。一度ajaxをコールしたらストップさせないといけないのですが、どこでこれを解放するかがキモでした。解放地点を間違えると、1度しか読み込まれなかったり、すぐに解放して重複コールしたりします。"load"は読み込み完了であって表示完了ではない事もキモです。

そこで安全地帯を作って、安全地帯まで"scroll"が移動したら解放するようにしました。コンテンツに応じて"triggerPoint"を調整すればストレスの無い動きになると思います。

##ありがとうございました
コメント頂きありがとうございました。
おかげで解決できました。
最後までお読み頂いた貴方もありがとうございます。

正しいやり方かどうか分かりませんが参考になれば幸いです。