Skip to content

Instantly share code, notes, and snippets.

@Twilyze
Last active September 9, 2017 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Twilyze/ce4a792e8d80ba3d25ce8c44a2e0c162 to your computer and use it in GitHub Desktop.
Save Twilyze/ce4a792e8d80ba3d25ce8c44a2e0c162 to your computer and use it in GitHub Desktop.
サイドバーに現在位置を表示して追尾する目次を設置する【目次記法対応版】 http://twilyze.hatenablog.jp/entry/hatena-blog-custom4
/* 目次(サイドバー) */
#sectionListSide ol {
padding: 0px;
margin: 0px;
list-style-type: none;
}
#sectionListSide ol .chapter {
padding-left: 10px;
}
#sectionListSide li > a {
color: #7d9ab7;
padding: 2px 0px 2px 4px;
display: block;
}
#sectionListSide li > a.current {
background-color: #f2f2f2;
}
#sectionListSide li > a:hover {
color: #263f5a;
background-color: #f2f2f2;
text-decoration: none;
}
<div id='sectionListSide'></div>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.slim.min.js'></script>
<script>
$(window).on('load', function() {
// 記事ページの時だけ
if (!$('body').hasClass('page-entry'))
return;
console.log('---sidebar toc---');
// スムーズスクロールと履歴の設定
function smoothScroll() {
$('html,body').animate({scrollTop: $(this.hash).offset().top}, 300, 'swing'); // 300ミリ秒かけてスクロール
window.history.pushState(null, null, this.hash); // 履歴追加
return false;
}
// オブジェクトの中身を数値に変換し足し合わせて返す
function sumObjProps(obj) {
var ret = 0;
for(var p in obj)
ret += parseInt(obj[p], 10);
return ret;
}
//-----------------------------------
// 目次作成
var lastModuleChild = $('#sectionListSide');
var mainInner = document.getElementById('main-inner');
var SECTION_QUERY = 'h3,h4,h5';
var list = [];
var currentLevel = 0;
var sectionTopArr = [];
var toc = mainInner.getElementsByClassName('table-of-contents');
var tocId = [];
var tocFlg = false;
// 目次記法を使ってる時はそちらからIDを取得
if (toc.length) {
var tocA = toc[0].getElementsByTagName('a');
var sliceIndex = tocA[0].href.split('#')[0].length + 1;
for(var i=0, len=tocA.length; i<len; i++) {
tocId.push(tocA[i].href.slice(sliceIndex));
tocA[i].addEventListener('click', smoothScroll);
}
tocFlg = true;
}
// 見出しを検索
$.each(mainInner.getElementsByClassName('entry-content')[0].querySelectorAll(SECTION_QUERY), function(i) {
var self = this;
var idName = 'section' + i;
var level = 0;
// a要素作成
if (tocFlg)
idName = tocId[i];
else
$(self).attr('id', idName);
list.push('<li><a href="#' + idName + '">' + $(self).text() + '</a>');
// 段落作成
if (self.nodeName.toLowerCase() == 'h4') {
level = 1;
} else if (self.nodeName.toLowerCase() == 'h5') {
level = 2;
}
while (currentLevel < level) {
list[i] = '<ol class="chapter">' + list[i];
currentLevel++;
}
while (currentLevel > level) {
list[i] = '</ol></li>' + list[i];
currentLevel--;
}
// 各見出しの位置を保存
sectionTopArr[i] = $(self).offset().top;
});
// 見出しが2つ以上無ければ目次を表示しない
if (list.length < 2) {
console.log('--toc hide--');
return;
}
// 目次追加前のサイドバー高さ合計と最後のモジュールの高さを保存
var SIDEBAR_HEIGHT = $('#box2').outerHeight(true);
var lastModule = lastModuleChild.parent().parent();
var LASTMODULE_PRE_HEIGHT = lastModule.outerHeight(true);
// サイドバーに目次を追加する
lastModuleChild.parent().before('<div class="hatena-module-title">目次</div>');
lastModuleChild.append('<div class="sectionList"><ol>' + list.join('') + '</ol></div>');
// スムーズスクロールの設定
var lastModule_a = $('a', lastModuleChild);
lastModule_a.on('click', smoothScroll);
//-----------------------------------
// 最後のモジュールを追従させる
var MARGIN = 10; // モジュールを固定した時の余白
// fixedの時見た目が変わらないように横幅を指定
lastModule.css('width', lastModule.width());
// モジュールの親要素からの相対位置にする
$('#box2-inner').css({'position': 'relative'});
var gh = $('#globalheader-container');
var HEADER_MENU_FIXED = gh.css('position') === 'fixed' ? gh.outerHeight(true) : 0;
// モジュールをウィンドウに固定する位置 と 下までスクロールした時にページへ固定する位置
var SCROLL_FIXED = lastModule.offset().top - HEADER_MENU_FIXED - MARGIN;
var SCROLL_ABSOLUTE = $('#container-inner').offset().top + $('#container-inner').outerHeight(true) - lastModule.outerHeight();
// var SCROLL_ABSOLUTE = $('.entry-footer').offset().top - lastModule.outerHeight();
console.log('SCROLL_FIXED:' + SCROLL_FIXED);
console.log('SCROLL_ABSOLUTE:' + SCROLL_ABSOLUTE);
//-----------------------------------
// スクロールイベント
var win = $(window);
var SCROLL_MARGIN = 50;
var SECTION_LAST_INDEX = sectionTopArr.length - 1;
var SCROLL_RANGE = $(document).height() - win.height();
// 現在位置を表示するためのクラス設定
var setCurrent = (function() {
var current = -1;
return function(i) {
if (i != current) {
current = i;
lastModule_a.removeClass('current');
lastModule_a.eq(i).addClass('current');
}
};
})();
setCurrent(0); // 初期位置登録
// モジュール固定位置が変わったかチェック
var checkPosition = (function() {
var current = -1;
return function(i) {
if (i != current) {
current = i;
return true;
}
};
})();
win.scroll(function () {
var scrollTop = win.scrollTop();
// 現在位置のクラス設定
if (scrollTop <= SCROLL_MARGIN) {
setCurrent(0);
}
else if ((SCROLL_RANGE - scrollTop) <= SCROLL_MARGIN) {
setCurrent(SECTION_LAST_INDEX);
}
else {
for (var i = SECTION_LAST_INDEX; i >= 0; i--) {
if (scrollTop > sectionTopArr[i] - SCROLL_MARGIN) {
setCurrent(i);
break;
}
}
}
// モジュールを追従させる
if (SCROLL_ABSOLUTE < scrollTop) {
if (checkPosition(0)) {
// ページに固定
lastModule.css({'position': 'absolute', 'top': SCROLL_ABSOLUTE + HEADER_MENU_FIXED - $('#box2').offset().top + MARGIN + 'px'});
}
} else if (SCROLL_FIXED < scrollTop) {
if (checkPosition(1)) {
// ウィンドウに固定
lastModule.css({'position': 'fixed', 'top': HEADER_MENU_FIXED + MARGIN + 'px'});
}
} else {
if (checkPosition(2)) {
// 初期位置
lastModule.css({'position': 'static'});
}
}
});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment