Skip to content

Instantly share code, notes, and snippets.

@suinua
Last active March 23, 2022 07:28
Show Gist options
  • Select an option

  • Save suinua/193d697b27deff9ca4f8f4982776b645 to your computer and use it in GitHub Desktop.

Select an option

Save suinua/193d697b27deff9ca4f8f4982776b645 to your computer and use it in GitHub Desktop.
Dartでスクロールに連動する目次
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
<script defer src="main.dart.js"></script>
</head>
<body>
<div class="wrap">
<div class="left">
<h1 id="Fruit">Fruit</h1>
<div class="box"></div>
<h2 id="Apple">Apple</h2>
<div class="box"></div>
<h2 id="Banana">Banana</h2>
<div class="box"></div>
<div class="box"></div>
<h1 id="Vegetable">Vegetable</h1>
<div class="box"></div>
<h2 id="Potato">Potato</h2>
<div class="box"></div>
<div class="box"></div>
<h2 id="Eggplant">Eggplant</h2>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div class="right">
<div class="toc">
<a class="toc-item" target-id="Fruit">Fruit</a>
<a class="toc-item" target-id="Apple">Apple</a>
<a class="toc-item" target-id="Banana">Banana</a>
<a class="toc-item" target-id="Vegetable">Vegetable</a>
<a class="toc-item" target-id="Potato">Potato</a>
<a class="toc-item" target-id="Eggplant">Eggplant</a>
</div>
</div>
</div>
</body>
</html>
import 'dart:html';
void main() {
var separatingElements = <Element>[];
//今回は<h1>と<h2>
separatingElements.addAll(querySelectorAll('h1'));
separatingElements.addAll(querySelectorAll('h2'));
var tocItems =
querySelectorAll('.toc-item').whereType<AnchorElement>().toList();
//スクロール時
document.onScroll.listen((event) {
var nearbyElement = getNearbyElement(separatingElements);
activeTocItem(tocItems, nearbyElement.id);
});
//tocクリック時
tocItems.forEach((tocItem) {
tocItem.onClick.listen((event) async {
var id = tocItem.getAttribute('target-id')!;
var target = document.getElementById(id)!;
window
.scrollTo({'top': target.offsetTop, 'left': 0, 'behavior': 'smooth'});
});
});
}
Element getNearbyElement(List<Element> elements) {
const topDetection = 50;
Element? result;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (result == null) {
result = element;
continue;
}
//要素とスクロール地点の差
var diff = element.offsetTop - window.scrollY;
//resultとスクロール地点の差
var resultDiff = result.offsetTop - window.scrollY;
if (0 < diff && diff < topDetection) return element;
/**
* Element
*
*
*
*
*
* -------スクロール位置----
* ↑
* 50px(topDetection)
* ↓
* Element(目次に表示させるのはコイツ)
*/
//スクロール地点より下にあったら除外,
/**
* Element(目次に表示させるのはコイツ)
*
* -------スクロール位置----
*↑
*50px(topDetection)オーバー
*↓
* Element 除外
*
*
* Element 除外
*/
if (diff > 0) continue;
//スクロール地点との差がより小さければresultに代入
if (diff.abs() < resultDiff.abs()) result = element;
}
return result!;
}
void activeTocItem(List<AnchorElement> tocItems, String separatingElementId) {
tocItems.forEach((tocItem) {
var targetId = tocItem.getAttribute('target-id')!;
if (targetId == separatingElementId) {
//activeクラスを付与
tocItem.classes.add('active');
} else {
//activeクラスを除去
tocItem.classes.removeWhere((element) => element == 'active');
}
});
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.wrap {
display: flex;
}
.left {
width: 80%;
}
.right {
width: 20%;
}
.toc {
position: sticky;
top: 20px;
}
.toc-item {
display: block;
color: white;
}
.active {
color: pink;
}
.box {
height: 300px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment