Files
argon-theme/assets/vendor/headindex/headindex.js
nanhaoluo f5902e0c12 fix: 修复移动端文章目录无法显示的问题
- 移动端目录改为从桌面端目录复制内容,避免 headIndex 插件重复初始化导致的冲突
- 添加移动端目录独立的点击事件处理
- 添加桌面端与移动端目录高亮状态同步机制(使用 MutationObserver)
- 恢复 headindex.js 为完整版本(与合并文件中的实现一致)
2026-01-15 23:14:03 +08:00

321 lines
13 KiB
JavaScript

/*!
* headIndex.js - 文章目录生成插件
* 为 Argon 主题提供文章目录功能
*
* 注意:此文件仅用于后台设置页面预览
* 前端页面使用的是 argon_js_merged.js 中的压缩版本
*/
;(function($, window) {
var headIndex = (function() {
function headIndex(element, options) {
this.settings = $.extend(true, $.fn.headIndex.default, options || {});
this.element = element;
this.init();
}
headIndex.prototype = {
init: function() {
this.articleWrap = $(this.settings.articleWrapSelector);
this.headerList = this.articleWrap.find(':header');
this.indexBox = $(this.settings.indexBoxSelector);
this.indexScrollBox = $(this.settings.indexScrollBoxSelector);
this.scrollBody = $(this.settings.scrollSelector);
this.scrollWrap = $(this.settings.scrollWrap);
this.manual = false;
this.mouseHovered = false;
if (this.indexBox.length === 0 || this.headerList.length === 0) {
return null;
}
this.initHeader();
this.event();
},
initHeader: function() {
for (var i = 0; i < this.headerList.length; i++, this.autoId++) {
this.headerList[i].id = this.headerList[i].id || "header-id-" + this.autoId;
this.headerList[i].topHeight = this.offsetTop(this.headerList[i]);
this.headerList[i].h = Number(this.headerList[i].tagName.charAt(1));
}
this.tempHtml = [];
this.buildHtml(this.buildTree());
var res = '<ul>' + this.tempHtml.join('') + '</ul>';
this.indexBox.html(res);
},
event: function() {
var that = this;
var manualValTimer = null;
this.indexBox.on('click.headindex', function(event) {
var target = $(event.target);
if (target.hasClass(that.settings.linkClass)) {
event.preventDefault();
var indexItem = target.parent('.' + that.settings.itemClass);
that.manual = true;
if (manualValTimer) {
clearTimeout(manualValTimer);
manualValTimer = null;
}
manualValTimer = setTimeout(function() {
that.manual = false;
}, 400);
that.current(indexItem);
that.scrollTo(event.target.getAttribute('href'));
}
});
this.indexScrollBox.on('mouseover', function(event) {
that.mouseHovered = true;
});
this.indexScrollBox.on('mouseleave', function(event) {
that.mouseHovered = false;
});
$(this.scrollWrap).scroll(function() {
if (that.manual) return;
that.updateCurrent();
});
that.updateCurrent();
},
updateTopHeight: function() {
var length = this.headerList.length;
var i;
if (length === 0) return;
if (this.headerList[0].topHeight === this.offsetTop(this.headerList[0]) &&
this.headerList[length - 1].topHeight === this.offsetTop(this.headerList[length - 1])) {
return;
}
if ((this.headerList[0].topHeight - this.offsetTop(this.headerList[0])) ===
(this.headerList[length - 1].topHeight - this.offsetTop(this.headerList[length - 1]))) {
var hx = this.offsetTop(this.headerList[0]) - this.headerList[0].topHeight;
for (i = 0; i < this.headerList.length; i++, this.autoId++) {
this.headerList[i].topHeight += hx;
}
return;
}
for (i = 0; i < this.headerList.length; i++, this.autoId++) {
this.headerList[i].topHeight = this.offsetTop(this.headerList[i]);
}
},
current: function(indexItem) {
var subBox, currentClass = 'current';
if (indexItem.length === 0 || indexItem.hasClass(currentClass)) {
return;
}
var otherCurrent = this.indexBox.find('li.' + currentClass);
if (otherCurrent.length > 0) {
otherCurrent.removeClass(currentClass);
}
this.indexBox.find('ul.open').removeClass('open');
subBox = indexItem.children('.' + this.settings.subItemBoxClass);
if (subBox.length > 0) {
subBox.addClass('open').slideDown();
}
var parentsBox = indexItem.parents('ul.' + this.settings.subItemBoxClass);
if (parentsBox.length > 0) {
parentsBox.addClass('open').slideDown();
}
subBox = this.indexBox.find('ul.' + this.settings.subItemBoxClass).not('.open');
if (subBox.length > 0) {
subBox.slideUp();
}
indexItem.addClass(currentClass);
if (this.mouseHovered) {
return;
}
var relativeOffsetTopToWrapper = indexItem.position().top;
var indexScrollBoxScrollTop = this.indexScrollBox.scrollTop();
var indexScrollBoxHeight = this.indexScrollBox.height();
if (relativeOffsetTopToWrapper < 20 + indexItem.height()) {
var target = indexScrollBoxScrollTop + relativeOffsetTopToWrapper - 20 - indexItem.height();
if (target < 30) {
target = 0;
}
this.indexScrollBox.stop().animate({scrollTop: target}, 'normal', 'easeOutExpo');
}
if (relativeOffsetTopToWrapper > indexScrollBoxHeight - 10) {
this.indexScrollBox.stop().animate({
scrollTop: indexScrollBoxScrollTop + relativeOffsetTopToWrapper - indexScrollBoxHeight + 10 + indexItem.height()
}, 'normal', 'easeOutExpo');
}
},
buildHtml: function(tree) {
if (tree === undefined || tree.length === 0) return;
for (var i = 0; i < tree.length; i++) {
this.tempHtml.push("<li class='" + this.settings.itemClass + "'><a no-pjax class='" + this.settings.linkClass + "' href='#" + tree[i].item.id + "'>" + tree[i].item.innerText + "</a>");
if (tree[i].children.length !== 0) {
this.tempHtml.push("<ul class='" + this.settings.subItemBoxClass + "'>");
this.buildHtml(tree[i].children);
this.tempHtml.push("</ul>");
}
this.tempHtml.push("</li>");
}
},
buildTree: function() {
var current = null, tempCur, indexTree = [];
for (var i = 0; i < this.headerList.length; i++) {
if (current == null) {
current = {
item: this.headerList[i],
parent: null,
children: []
};
indexTree.push(current);
continue;
}
if (current.item.h < this.headerList[i].h) {
tempCur = {
item: this.headerList[i],
parent: current,
children: []
};
current.children.push(tempCur);
current = tempCur;
continue;
}
if (current.item.h === this.headerList[i].h) {
tempCur = {
item: this.headerList[i],
parent: current.parent,
children: []
};
((current.parent && current.parent.children) || indexTree).push(tempCur);
current = tempCur;
continue;
}
while (current != null && current.item.h > this.headerList[i].h) {
current = current.parent;
}
if (current == null) {
current = {
item: this.headerList[i],
parent: null,
children: []
};
indexTree.push(current);
continue;
}
i--;
}
return indexTree;
},
search: function(start, end, findValue) {
if (this.headerList.length === 0) return null;
if (end - start <= 1) {
if (this.headerList[end].topHeight < findValue) {
return this.headerList[end];
}
return this.headerList[start];
}
if (start < end) {
var middleIndex = parseInt((start + end) / 2);
var middleValue = this.headerList[middleIndex].topHeight;
if (findValue < middleValue) {
end = middleIndex;
} else if (findValue > middleValue) {
start = middleIndex;
} else {
return this.headerList[middleIndex];
}
return this.search(start, end, findValue);
}
},
offsetTop: function(elem) {
return $(elem).offset().top - this.settings.offset;
},
scrollTo: function(eid) {
this.scrollBody.stop().animate({
scrollTop: this.offsetTop(document.getElementById(eid.substr(1))) + 8
}, 'normal', 'easeOutExpo');
},
updateCurrent: function() {
var scrollTop = this.scrollBody.scrollTop();
this.updateTopHeight();
var find = this.search(0, this.headerList.length - 1, scrollTop);
if (!find) {
return;
}
var indexItem = this.indexBox.find('a[href="#' + find.id + '"]').parent('li.' + this.settings.itemClass);
this.current(indexItem);
},
clean: function() {
if (this.element) {
this.element.data("headIndex", null);
}
},
ignoreScrollEvent: function(ignore) {
if (ignore) {
this.manual = true;
} else {
this.manual = false;
this.updateCurrent();
}
}
};
headIndex.prototype.autoId = 1;
return headIndex;
})();
$.fn.headIndex = function(options) {
return this.each(function() {
var $this = $(this),
instance = $this.data("headIndex");
instance = new headIndex($this, options);
$this.data("headIndex", instance);
if ($.type(options) === "string") return instance[options]();
});
};
$.fn.headIndex.default = {
articleWrapSelector: ".article-wrap",
indexBoxSelector: ".index-box",
indexScrollBoxSelector: "#leftbar_part2_inner",
scrollSelector: 'body,html',
scrollWrap: window,
subItemBoxClass: "index-subItem-box",
itemClass: "index-item",
linkClass: "index-link",
offset: 0
};
})(jQuery, window);