首页「更多栏目」tab 与「排序方式」切换的骨架占位与参考主题 zibll 有实质差异,
根因不是几何宽度,而是三处结构/契约错位:
1. 骨架块自身结构与参考主题不同。card 分支用了 `<posts>` 元素 + `.post-graphic`
包裹层(列表变体,带 margin-right:20px),而参考主题 card 骨架是
`<div class="posts-item card">` 且 `.item-thumbnail` 是直接子元素。
→ card 骨架右侧多出一条真实卡片没有的 20px 沟槽。
2. 骨架块被错误地打上 `ajax-item`。参考主题的占位块没有该类。它会让
`find('.ajax-item')` / ajax-list 的 `inject()` 把骨架当成真实条目。
3. 排序切换的骨架被预渲染节点顶替。aksrt_render_pagination() 在 .ajax-pag 内
渲染了一个写死 2 条、写死 list 形状的 .post_ajax_loader;runtime post_ajax()
会先在容器内 find('.post_ajax_loader'),找到就复用并丢弃调用方按视口动态
拼出的骨架 → 切排序永远是 2 条 list 骨架,card 布局下也是、与视口宽度无关。
参考主题 zib_paging() 根本没有 loader 兄弟节点。
改动:
- inc/helpers/posts-renderer/markup.php:aksrt_render_placeholder() 的
card/list 分支改为与 zib_get_post_placeholder() 逐字等价的结构;去掉
ajax-item 类、card 分支的 .post-graphic 包裹层,元素改用 <div>。
- inc/helpers/template-tags/pagination-permissions.php:删除预渲染 loader,
与参考主题 zib_paging() 一致 —— 只渲染翻页控件。
- assets/js/core/pagination.js:找不到骨架节点时构造与 runtime/main.js 同款
的文章骨架(卡片 8/6/4 条、列表 4/3 条,按视口与参考主题同阈值),
不再退化为「转圈 + 文案」这个第二套加载语言;仅评论等非文章面保留该兜底。
- assets/js/core/pagination.js:为排序菜单(.ajax-option)与 tab 触发器
(.post_ajax_trigger)增加避让 —— 它们归 legacy runtime 管线,
否则一次点击发两个请求(与 ajax-list.js 的既有避让对称)。
- assets/js/runtime/main.js:post_ajax 的显隐由 fadeIn()/hide() 改为
.is-loading 类切换。前者写 inline display,优先级高于类规则,一旦残留
骨架永远打不开,正是骨架契约明令禁止的失败模式。
归档/分类筛选面原本另有一套独立加载语言(spinner 遮罩 + opacity 淡出),
本次一并统一到同一条 .post_ajax_loader + .is-loading 契约:
- inc/helpers/aksrt-archive.php:删除 show_loading 标记位与遮罩 DOM,
改为在分页之后渲染文章骨架。
- assets/js/core/archive.js:showLoading/hideLoading 只切 .is-loading;
fetchPosts() 清空内容时保留骨架与分页节点(与参考主题 success 只 hide
不删的生命周期一致),原先会连带删掉骨架。
- assets/css/frontend/layout.css:删除 .archive__ajax-wrap.is-loading
的 opacity 淡出与 spinner 遮罩规则。
- category.php / tag.php:删除已失效的 show_loading 实参。
验证:php -l 全部通过;php tools/audit-admin-contracts.php errors=0;
契约测试 post-visual(22) / asset-ledger(16) / message-center(8) /
lazyload(7) / frontend-lifecycle(13) / zibll-modern-widgets(10) /
page-display(18) / a11y 全绿;已重跑 build:public 刷新产物。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
235 lines
8.6 KiB
JavaScript
235 lines
8.6 KiB
JavaScript
/**
|
||
* AKSRT Archive AJAX Filter
|
||
*/
|
||
(function () {
|
||
"use strict";
|
||
|
||
function initArchiveFilter(root) {
|
||
var scope = root || document;
|
||
var bar = scope.querySelector(".archive-filter-bar--ajax[data-ajax-archive]");
|
||
if (!bar || bar.dataset.aksrtArchiveInit === "1") return;
|
||
bar.dataset.aksrtArchiveInit = "1";
|
||
|
||
var archiveType = bar.dataset.ajaxArchive || "category";
|
||
var termId = parseInt(bar.dataset.termId, 10) || 0;
|
||
var wrap = scope.querySelector("[data-ajax-wrap]");
|
||
if (!wrap) return;
|
||
|
||
// 归档面与首页共用同一套骨架契约:.post_ajax_loader + .is-loading,
|
||
// 不再使用独立 spinner 遮罩 / opacity 淡出。
|
||
var loading = wrap.querySelector(".post_ajax_loader");
|
||
var REST_BASE = "/wp-json/aksrt/v1/archive/posts";
|
||
var emptyText = archiveType === "tag" ? "该标签下暂无文章" : "该分类下暂无文章";
|
||
|
||
var state = {
|
||
filter_cat: "",
|
||
filter_tag: "",
|
||
custom: {},
|
||
sort: "date",
|
||
page: 1
|
||
};
|
||
|
||
var activeCat = bar.querySelector("[data-filter-cat].is-active");
|
||
if (activeCat) state.filter_cat = activeCat.dataset.filterCat || "";
|
||
var activeTag = bar.querySelector("[data-filter-tag].is-active");
|
||
if (activeTag) state.filter_tag = activeTag.dataset.filterTag || "";
|
||
var activeSort = bar.querySelector("[data-filter-sort].is-active");
|
||
if (activeSort) state.sort = activeSort.dataset.filterSort || "date";
|
||
// 自定义高级筛选维度:以当前 URL 参数为准(SSR active 状态与 URL 一致)。
|
||
if (bar.querySelector("[data-filter-dim]")) {
|
||
new URLSearchParams(window.location.search).forEach(function (value, key) {
|
||
if (bar.querySelector('[data-filter-dim="' + key + '"]')) {
|
||
state.custom[key] = value;
|
||
}
|
||
});
|
||
}
|
||
|
||
function buildUrl() {
|
||
var params = new URLSearchParams();
|
||
params.set("archive_type", archiveType);
|
||
params.set("term_id", String(termId));
|
||
if (state.filter_cat) params.set("filter_cat", state.filter_cat);
|
||
if (state.filter_tag) params.set("filter_tag", state.filter_tag);
|
||
if (Object.keys(state.custom).length) params.set("custom_filter", JSON.stringify(state.custom));
|
||
|
||
params.set("sort", state.sort || "date");
|
||
params.set("page", String(state.page));
|
||
return REST_BASE + "?" + params.toString();
|
||
}
|
||
|
||
function updateActiveChips() {
|
||
bar.querySelectorAll("[data-filter-cat]").forEach(function (el) {
|
||
el.classList.toggle("is-active", (el.dataset.filterCat || "") === state.filter_cat);
|
||
});
|
||
bar.querySelectorAll("[data-filter-tag]").forEach(function (el) {
|
||
el.classList.toggle("is-active", (el.dataset.filterTag || "") === state.filter_tag);
|
||
});
|
||
bar.querySelectorAll("[data-filter-sort]").forEach(function (el) {
|
||
el.classList.toggle("is-active", (el.dataset.filterSort || "date") === state.sort);
|
||
});
|
||
bar.querySelectorAll("[data-filter-dim]").forEach(function (el) {
|
||
var current = state.custom[el.dataset.filterDim] || "";
|
||
el.classList.toggle("is-active", (el.dataset.filterDimValue || "") !== "" && (el.dataset.filterDimValue || "") === current);
|
||
});
|
||
}
|
||
|
||
function showLoading() {
|
||
if (loading) loading.classList.add("is-loading");
|
||
}
|
||
|
||
function hideLoading() {
|
||
if (loading) loading.classList.remove("is-loading");
|
||
}
|
||
|
||
function rebuildEmptyState() {
|
||
var noResults = document.createElement("div");
|
||
noResults.className = "no-results content-stream__empty";
|
||
noResults.innerHTML = "<p>" + emptyText + "</p>";
|
||
wrap.appendChild(noResults);
|
||
}
|
||
|
||
function fetchPosts(pushState) {
|
||
showLoading();
|
||
fetch(buildUrl(), {
|
||
method: "GET",
|
||
headers: { Accept: "application/json" },
|
||
credentials: "same-origin"
|
||
})
|
||
.then(function (r) {
|
||
if (!r.ok) throw new Error("HTTP " + r.status);
|
||
return r.json();
|
||
})
|
||
.then(function (data) {
|
||
hideLoading();
|
||
if (data.html !== undefined) {
|
||
// 只清真实内容;骨架(.post_ajax_loader)与分页控件保留复用 ——
|
||
// 与参考主题 post_ajax success 只 hide 不删的生命周期一致。
|
||
var keep = function (node) {
|
||
return node === loading ||
|
||
node.classList.contains("post_ajax_loader") ||
|
||
node.classList.contains("ajax-pag");
|
||
};
|
||
Array.from(wrap.children).forEach(function (child) {
|
||
if (!keep(child)) wrap.removeChild(child);
|
||
});
|
||
if (data.html) {
|
||
var temp = document.createElement("div");
|
||
temp.innerHTML = data.html;
|
||
while (temp.firstChild) wrap.appendChild(temp.firstChild);
|
||
} else {
|
||
rebuildEmptyState();
|
||
}
|
||
// legacy 事件顺序:先 post_ajax.ed,再全局 auto_fun 重扫
|
||
wrap.dispatchEvent(new CustomEvent("post_ajax.ed", { bubbles: true }));
|
||
if (typeof window.auto_fun === "function") window.auto_fun();
|
||
}
|
||
if (pushState !== false) {
|
||
var url = new URL(window.location.href);
|
||
url.searchParams.delete("orderby");
|
||
url.searchParams.delete("order");
|
||
url.searchParams.delete("paged");
|
||
url.searchParams.delete("aksrt_filter_cat");
|
||
url.searchParams.delete("aksrt_filter_tag");
|
||
bar.querySelectorAll("[data-filter-dim]").forEach(function (el) {
|
||
url.searchParams.delete(el.dataset.filterDim);
|
||
});
|
||
if (state.filter_cat) url.searchParams.set("aksrt_filter_cat", state.filter_cat);
|
||
if (state.filter_tag) url.searchParams.set("aksrt_filter_tag", state.filter_tag);
|
||
Object.keys(state.custom).forEach(function (key) {
|
||
url.searchParams.set(key, state.custom[key]);
|
||
});
|
||
if (state.sort) url.searchParams.set("orderby", state.sort);
|
||
if (state.page > 1) url.searchParams.set("paged", String(state.page));
|
||
history.pushState({ aksrtArchive: true, state: Object.assign({}, state) }, "", url.toString());
|
||
}
|
||
})
|
||
.catch(function () {
|
||
hideLoading();
|
||
});
|
||
}
|
||
|
||
bar.addEventListener("click", function (e) {
|
||
var chip = e.target.closest("[data-filter-cat], [data-filter-tag], [data-filter-sort], [data-filter-dim]");
|
||
if (!chip) return;
|
||
e.preventDefault();
|
||
|
||
var changed = false;
|
||
if (chip.dataset.filterCat !== undefined) {
|
||
var newCat = chip.dataset.filterCat;
|
||
if (newCat !== state.filter_cat) {
|
||
state.filter_cat = newCat;
|
||
changed = true;
|
||
}
|
||
}
|
||
if (chip.dataset.filterTag !== undefined) {
|
||
var newTag = chip.dataset.filterTag;
|
||
if (newTag !== state.filter_tag) {
|
||
state.filter_tag = newTag;
|
||
changed = true;
|
||
}
|
||
}
|
||
if (chip.dataset.filterSort !== undefined) {
|
||
var newSort = chip.dataset.filterSort || "date";
|
||
if (newSort !== state.sort) {
|
||
state.sort = newSort;
|
||
changed = true;
|
||
}
|
||
}
|
||
if (chip.dataset.filterDim !== undefined) {
|
||
var dimKey = chip.dataset.filterDim;
|
||
var dimValue = chip.dataset.filterDimValue || "";
|
||
if (dimValue !== (state.custom[dimKey] || "")) {
|
||
if (dimValue === "") {
|
||
delete state.custom[dimKey];
|
||
} else {
|
||
state.custom[dimKey] = dimValue;
|
||
}
|
||
changed = true;
|
||
}
|
||
}
|
||
|
||
if (changed) {
|
||
state.page = 1;
|
||
updateActiveChips();
|
||
fetchPosts(true);
|
||
}
|
||
});
|
||
|
||
wrap.addEventListener("click", function (e) {
|
||
var pageLink = e.target.closest(".pagination a, .nav-links a, a.page-numbers");
|
||
if (!pageLink || !wrap.contains(pageLink)) return;
|
||
var href = pageLink.getAttribute("href");
|
||
if (!href) return;
|
||
|
||
var match = href.match(/[?&]paged=(\d+)/);
|
||
if (match) {
|
||
e.preventDefault();
|
||
state.page = parseInt(match[1], 10) || 1;
|
||
fetchPosts(true);
|
||
}
|
||
});
|
||
|
||
window.addEventListener("popstate", function (e) {
|
||
if (e.state && e.state.aksrtArchive) {
|
||
state = Object.assign(state, e.state.state);
|
||
updateActiveChips();
|
||
fetchPosts(false);
|
||
}
|
||
});
|
||
}
|
||
|
||
function init(root) {
|
||
initArchiveFilter(root);
|
||
}
|
||
|
||
if (document.readyState === "loading") {
|
||
document.addEventListener("DOMContentLoaded", function () { init(); });
|
||
} else {
|
||
init();
|
||
}
|
||
// AJAX 注入新内容后由 auto_fun 重扫(bar 级 data 标记保证幂等)。
|
||
if (window.jQuery) {
|
||
window.jQuery(document).on("auto_fun", function () { init(); });
|
||
}
|
||
})();
|