はてなブログ「続きを読む」クリックで記事ページ「続き」位置に飛ばす

当サイトはトップページに記事を表示していませんので問題ないのですが、別グログの「そんなにも褒めないよ。映画評」では、トップページに最新の1記事とその下に最新記事一覧を表示しています。

その際、記事の途中に「続きを読む」を挿入しています。

で、この「続きを読む」は、記事タイトルと同様に記事ページへのリンクになっていますので、当然、ページのトップへ飛んでしまいます。それを「続きを読む」の位置へ飛ばそうと思います。


(2017/10/30)記事中の Javascript に変数宣言がもれている箇所がありました



はてなブログ「続きを読む」の仕様

はてなブログの「続きを読む」がどうなっているかを見てみますと、

  • ツールバーの「続きを読む」アイコンをクリックすると <!-- more --> が挿入される
  • トップページでは、<a class="entry-see-more" href="記事URL">続きを読む</a> に変換される
  • 記事ページでは削除される

となっています。


記事ページに <!-- more --> が残っていれば、ページを開いた時にそこまで飛べばいいのですが、痕跡がなければどうしようもありません。


「続きを読む」クリックで記事ページ「続き」位置に飛ばす仕様

で、どうするかといいますと、

  • トップページなら
  • 記事本文だけを配列に入れ
  • 各記事内で「続きを読む」を探し、あれば
  • 「続きを読む」のリンクにクエリパラメータ readmore=本文の子要素の数を追加する

としておけば、「続きを読む」をクリックした時だけ、「続きを読む」までの記事本文の子要素数、たとえば、「続きを読む」が10個目の子要素であれば、 ?readmore=10 などのクエリパラメータを持った URL で呼ばれます。


次に、記事ページですが、

  • URL にクエリパラメータがあれば
  • パラメーターを配列に入れ
  • 各要素に対して、キーが readmore なら
  • 本文の パラメータの値 番目の子要素までページをスクロールする

これでいけるはずです。


「続き」位置までスクロールする Javascript

/**
* 即時関数
*/
(function(){
  // トップページなら
  if (document.body.classList.contains('page-index')) {
    // 記事の数だけ繰り返す
    Array.prototype.forEach.call(document.getElementsByClassName('entry-content'), function(elem){
      var seeMore = elem.getElementsByClassName('entry-see-more');
      // 「続きを読む」があれば
      if (seeMore.length !== 0) {
        // リンクにクエリパラメータを追加する
        seeMore[0].href = seeMore[0].href + '?readmore=' + elem.childElementCount;
      }
    });
  }
}());

/**
* 読み込みが完了したら実行
* 他に onload があれば上書きしてしまうので注意
*/
window.onload = function(){
  // クエリパラメータを取得
  var query = location.search.substring(1);
  // クエリパラメータがあれば
  if (query) {
    // 今回はパラメータはひとつなので必要ないが念のため
    var params = query.split('&');
    for (i = 0; i < params.length; i++) {
      var item = params[i].split('=');
      // パラメータキーが readmore なら
      if (item[0] === 'readmore') {
        // 「続きを読む」のひとつ前の要素を取得しクラス readmore を付加する
        var elem = document.getElementsByClassName('entry-content')[0].children[item[1]-1];
        elem.classList.add('readmore');
        // 「続きを読む」のひとつ前の要素の絶対座標までスクロールさせる
        smoothScroll(elem.getBoundingClientRect().top);
      }
    }
  }
};

/**
* 指定要素までスクロールする関数
* @param {number} targetY (要素の絶対y座標)
* パラメータを 0 とすればページトップまで飛ぶ
*/
function smoothScroll(targetY) {
  var startY = window.pageYOffset;
  var f = ( targetY > startY ) ? true : false; // 'down' = true : 'up' = false
  setTimeout(function(){
    if( f && (startY <= targetY)){
      startY = startY + (targetY - startY) / 20 + 1;
      window.scrollTo(0, startY);
      setTimeout(arguments.callee, 10);
    } else if ( !f && startY >= targetY){
      startY = startY - (startY - targetY) / 20 - 1;
      window.scrollTo(0, startY);
      setTimeout(arguments.callee, 10);
    }else{
      window.scrollTo(0, targetY);
    }
  }, 10);
}


window.onload の部分は、他にクエリパラメータがないことが分かっていますので、もっと簡潔に

window.onload = function(){
  var query = location.search.substring(1);
  if (query) {
    var item = query.split('=');
    var elem = document.getElementsByClassName('entry-content')[0].children[item[1]-1];
    elem.classList.add('readmore');
    smoothScroll(elem.getBoundingClientRect().top);
  }
};

でもいけます。また、スクロール後の位置を調整する場合は、

    smoothScroll(elem.getBoundingClientRect().top - 100);

とすれば、「続き」位置の上部に 100px の余裕が出来ます。

また、スクロールが鬱陶しかったり、ぎこぎこする場合は、

//        smoothScroll(elem.getBoundingClientRect().top);
        window.scrollTo(0, elem.getBoundingClientRect().top);

として、直接飛ばしてしまえばいいです。

さらに、読み込み完了を待たずに移動したい場合は、window.onload 内のコードをすべて即時関数の中に入れてしまえば、読み込み完了を待たずに移動するはずです。


「続き」の読み始めをわかりやすく装飾する

「続き」の読み始めにクラス名 readmore を付加してありますので、CSS で「続き」の読み始め位置をわかりやすくすることが出来ます。


サンプルCSS

p.readmore::before {
    content: '\f006';
    font-family: 'blogicon';
    color: red;
    margin-right: 1rem;
    text-shadow: 0.5rem 0 0 red;
}

rem は、empx など、使用している単位に変更してください。