Instantly share code, notes, and snippets.
Last active
September 9, 2017 17:01
-
Save Twilyze/ce4a792e8d80ba3d25ce8c44a2e0c162 to your computer and use it in GitHub Desktop.
サイドバーに現在位置を表示して追尾する目次を設置する【目次記法対応版】 http://twilyze.hatenablog.jp/entry/hatena-blog-custom4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 目次(サイドバー) */ | |
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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