WordPress的文章是自带“置顶”功能的,但它似乎仅支持某些主题的首页。如果使用了某些插件排序,比如elementor制作文章列表,是不支持的。所以我开发了这个功能,利用WordPress原有的“置顶”设置让哪些文章置顶,并且让Elementor的Archive Post、Loop Grid、Loop Carousel都支持。
如何支持函数我就不赘述了,functions.php
/**
* Elementor Loop Grid & Loop Carousel:置顶文章优先显示 + CSS 标记
*
* [NEW] 同时支持 Current Query(Loop Grid)和 Custom Query(Loop Carousel)
* 配置化版本:支持灵活控制生效范围
*/
// ========== 配置区域:在这里控制生效范围 ==========
$sticky_config = [
'enable_home' => true, // 首页
'enable_all_archives' => false, // 所有归档页(包括分类、标签、作者、日期等)
'enable_all_categories' => false, // 所有分类归档页
'enable_all_tags' => false, // 所有标签归档页
'enable_all_singles' => false, // 所有文章页(单篇 post)
'enable_all_pages' => false, // 所有 Page 页面(单页)
'enable_author' => false, // 作者归档页
'enable_date' => false, // 日期归档页
'enable_search' => false, // 搜索结果页
// [NEW] Loop Carousel / Custom Query 支持
'enable_custom_queries' => true, // 是否对 Elementor 的自定义查询(Custom Query)也生效
// 指定分类 slug(留空 = 不限定;填入则只在这些分类树内生效)
'specific_categories' => ['products'],
// 指定标签 slug(留空 = 不限定)
'specific_tags' => [],
// 指定自定义分类法 taxonomy(留空 = 不限定)
'specific_taxonomies' => [],
// 调试模式:true = 在页面底部显示调试信息(仅管理员可见)
'debug_mode' => false,
];
// ========== 以下代码无需修改,自动根据上面配置生效 ==========
// 全局变量收集调试消息
global $sticky_debug_messages;
$sticky_debug_messages = [];
// 自定义调试函数(收集消息)
function sticky_debug_log($message) {
global $sticky_debug_messages;
$sticky_debug_messages[] = $message;
}
// [NEW] 通用函数:判断当前查询是否应该启用置顶逻辑
function sticky_should_apply($query, $sticky_config) {
// 如果是后台,跳过
if (is_admin() && !wp_doing_ajax()) return false;
$is_main_query = $query->is_main_query();
$is_custom_query = !$is_main_query && $sticky_config['enable_custom_queries'];
// 必须是主查询 或 允许自定义查询
if (!$is_main_query && !$is_custom_query) return false;
// 只处理文章类型为 post 的查询
$post_type = $query->get('post_type');
if (!empty($post_type) && $post_type !== 'post' && !in_array('post', (array)$post_type)) {
return false;
}
return true;
}
// [NEW] 通用函数:检查当前页面/查询类型是否在允许范围内
function sticky_check_page_type($query, $sticky_config) {
$is_allowed = false;
$page_type = '';
$cat_ids_to_check = [];
// 1. 首页
if ($query->is_home() && $sticky_config['enable_home']) {
$is_allowed = true;
$page_type = 'home';
}
// 2. 所有归档页
if ($query->is_archive() && $sticky_config['enable_all_archives']) {
$is_allowed = true;
$page_type = 'archive';
}
// 3. 分类归档页
if ($query->is_category() || $query->get('category_name') || $query->get('cat')) {
$category_name = $query->get('category_name');
$cat_id = $query->get('cat');
$term = null;
if ($category_name) {
$term = get_term_by('slug', $category_name, 'category');
} elseif ($cat_id) {
$term = get_term($cat_id, 'category');
}
if ($term && !is_wp_error($term)) {
$target_cat_id = $term->term_id;
// 3a. 所有分类归档
if ($sticky_config['enable_all_categories']) {
$is_allowed = true;
$page_type = 'category (all)';
}
// 3b. 指定分类树
if (!empty($sticky_config['specific_categories'])) {
if (in_array($term->slug, $sticky_config['specific_categories'])) {
$is_allowed = true;
$page_type = 'category (specific: ' . $term->slug . ')';
} else {
// 检查祖先
$ancestors = get_ancestors($target_cat_id, 'category');
foreach ($ancestors as $ancestor_id) {
$ancestor_term = get_term($ancestor_id, 'category');
if ($ancestor_term && in_array($ancestor_term->slug, $sticky_config['specific_categories'])) {
$is_allowed = true;
$page_type = 'category (child of: ' . $ancestor_term->slug . ')';
break;
}
}
}
}
// 获取该分类及其所有子分类的ID
if ($is_allowed) {
$cat_ids_to_check[] = $target_cat_id;
$children = get_term_children($target_cat_id, 'category');
if (!is_wp_error($children) && !empty($children)) {
$cat_ids_to_check = array_merge($cat_ids_to_check, $children);
}
}
}
}
// 4. 标签归档页
if ($query->is_tag() || $query->get('tag')) {
if ($sticky_config['enable_all_tags']) {
$is_allowed = true;
$page_type = 'tag (all)';
} elseif (!empty($sticky_config['specific_tags'])) {
$tag_slug = $query->get('tag');
if (in_array($tag_slug, $sticky_config['specific_tags'])) {
$is_allowed = true;
$page_type = 'tag (specific: ' . $tag_slug . ')';
}
}
}
// 5. 自定义分类法归档页
if (($query->is_tax() || $query->get('taxonomy')) && !empty($sticky_config['specific_taxonomies'])) {
$taxonomy = $query->get('taxonomy');
$term_slug = $query->get('term');
if ($taxonomy && $term_slug) {
if (isset($sticky_config['specific_taxonomies'][$taxonomy])
&& in_array($term_slug, $sticky_config['specific_taxonomies'][$taxonomy])) {
$is_allowed = true;
$page_type = 'custom taxonomy (' . $taxonomy . ': ' . $term_slug . ')';
}
}
}
// 6. 作者归档页
if ($query->is_author() && $sticky_config['enable_author']) {
$is_allowed = true;
$page_type = 'author';
}
// 7. 日期归档页
if ($query->is_date() && $sticky_config['enable_date']) {
$is_allowed = true;
$page_type = 'date';
}
// 8. 搜索结果页
if ($query->is_search() && $sticky_config['enable_search']) {
$is_allowed = true;
$page_type = 'search';
}
// 9. 单篇文章页
if ($query->is_single() && !$query->is_page() && $sticky_config['enable_all_singles']) {
$is_allowed = true;
$page_type = 'single post';
}
// 10. 单个 Page 页面
if ($query->is_page() && $sticky_config['enable_all_pages']) {
$is_allowed = true;
$page_type = 'page';
}
// [NEW] 11. 自定义查询(Loop Carousel 等)
// 如果是自定义查询,检查是否有分类参数
if (!$query->is_main_query() && $sticky_config['enable_custom_queries']) {
// 检查查询参数中的分类
$cat = $query->get('cat');
$category_name = $query->get('category_name');
if ($cat || $category_name) {
$term = null;
if ($category_name) {
$term = get_term_by('slug', $category_name, 'category');
} elseif ($cat) {
$term = get_term($cat, 'category');
}
if ($term && !is_wp_error($term)) {
$target_cat_id = $term->term_id;
// 检查是否在允许的分类树内
if (!empty($sticky_config['specific_categories'])) {
if (in_array($term->slug, $sticky_config['specific_categories'])) {
$is_allowed = true;
$page_type = 'custom query (category: ' . $term->slug . ')';
} else {
$ancestors = get_ancestors($target_cat_id, 'category');
foreach ($ancestors as $ancestor_id) {
$ancestor_term = get_term($ancestor_id, 'category');
if ($ancestor_term && in_array($ancestor_term->slug, $sticky_config['specific_categories'])) {
$is_allowed = true;
$page_type = 'custom query (child of: ' . $ancestor_term->slug . ')';
break;
}
}
}
} elseif ($sticky_config['enable_all_categories']) {
$is_allowed = true;
$page_type = 'custom query (category)';
}
if ($is_allowed) {
$cat_ids_to_check[] = $target_cat_id;
$children = get_term_children($target_cat_id, 'category');
if (!is_wp_error($children) && !empty($children)) {
$cat_ids_to_check = array_merge($cat_ids_to_check, $children);
}
}
}
}
// 如果没有分类限定,但启用了所有归档或首页,也允许
if (!$is_allowed && ($sticky_config['enable_all_archives'] || $sticky_config['enable_home'])) {
$is_allowed = true;
$page_type = 'custom query (general)';
}
}
return [
'is_allowed' => $is_allowed,
'page_type' => $page_type,
'cat_ids' => $cat_ids_to_check
];
}
// pre_get_posts 动作(设置条件和查询变量)
add_action('pre_get_posts', function ($query) use ($sticky_config) {
// 检查是否应该处理这个查询
if (!sticky_should_apply($query, $sticky_config)) return;
// 检查页面类型是否允许
$check_result = sticky_check_page_type($query, $sticky_config);
if (!$check_result['is_allowed']) {
sticky_debug_log('[Sticky Debug] Skipping: Page type not in allowed scope');
return;
}
$page_type = $check_result['page_type'];
$cat_ids_to_check = $check_result['cat_ids'];
sticky_debug_log('[Sticky Debug] Sticky processing triggered for: ' . $page_type);
// ========== 获取并过滤置顶文章 ==========
$sticky_ids = get_option('sticky_posts');
if (empty($sticky_ids)) {
sticky_debug_log('[Sticky Debug] No sticky posts found');
return;
}
sticky_debug_log('[Sticky Debug] All sticky IDs: ' . implode(',', $sticky_ids));
// 过滤出属于当前查询的置顶文章
$sticky_in_this_query = [];
foreach ($sticky_ids as $post_id) {
if (get_post_type($post_id) !== 'post') continue;
// 如果有分类限定,检查文章是否属于该分类树
if (!empty($cat_ids_to_check)) {
$has_match = false;
foreach ($cat_ids_to_check as $cat_id) {
if (has_term($cat_id, 'category', $post_id)) {
$has_match = true;
break;
}
}
if (!$has_match) continue;
}
$sticky_in_this_query[] = $post_id;
}
if (empty($sticky_in_this_query)) {
sticky_debug_log('[Sticky Debug] No sticky posts in this query scope');
return;
}
sticky_debug_log('[Sticky Debug] Sticky IDs for this query: ' . implode(',', $sticky_in_this_query));
// ========== 分页处理 ==========
$paged = max(1, (int) $query->get('paged'), (int) get_query_var('paged'));
sticky_debug_log('[Sticky Debug] Current page: ' . $paged);
if ($paged > 1) {
// 非第一页:排除置顶文章
$existing_exclude = $query->get('post__not_in');
if (!is_array($existing_exclude)) {
$existing_exclude = !empty($existing_exclude) ? [$existing_exclude] : [];
}
$query->set('post__not_in', array_merge($existing_exclude, $sticky_in_this_query));
sticky_debug_log('[Sticky Debug] Non-first page: Excluded stickies');
return;
}
// 第一页:设置查询变量,用于 posts_clauses 重排
$query->set('sticky_posts_for_sort', $sticky_in_this_query);
sticky_debug_log('[Sticky Debug] First page: Set sticky_posts_for_sort for reordering');
}, 1); // [NEW] 优先级改为 1,确保在 Elementor 查询之前执行
// posts_clauses 过滤器(修改 SQL ORDER BY 以强制置顶在前)
add_filter('posts_clauses', function ($clauses, $query) {
global $sticky_debug_messages, $wpdb;
$sticky_ids = $query->get('sticky_posts_for_sort');
if (empty($sticky_ids)) return $clauses;
sticky_debug_log('[Sticky Debug] Modifying ORDER BY for sticky IDs: ' . implode(',', $sticky_ids));
// 构建 CASE 语句
$case = 'CASE ';
foreach ($sticky_ids as $index => $id) {
$case .= "WHEN {$wpdb->posts}.ID = $id THEN $index ";
}
$case .= 'ELSE ' . (count($sticky_ids) + 1) . ' END';
// 修改 ORDER BY
$clauses['orderby'] = "$case ASC, " . $clauses['orderby'];
sticky_debug_log('[Sticky Debug] Modified ORDER BY: ' . $clauses['orderby']);
return $clauses;
}, 10000, 2);
// 给置顶文章自动加 CSS class:is-sticky-post
add_filter('post_class', function ($classes, $class, $post_id) {
if (is_sticky($post_id)) {
$classes[] = 'is-sticky-post';
}
return $classes;
}, 10, 3);
// 调试输出(仅管理员,且 debug_mode 开启时)
if ($sticky_config['debug_mode']) {
add_action('wp_footer', function () {
global $sticky_debug_messages;
if (!current_user_can('manage_options')) return;
echo '<pre style="background: #f0f0f0; padding: 10px; margin: 20px; border: 1px solid #ccc; font-size: 12px; white-space: pre-wrap; z-index: 9999; position: relative;">';
echo "<strong>Sticky Debug Log:</strong>\n";
if (!empty($sticky_debug_messages)) {
echo implode("\n", $sticky_debug_messages);
} else {
echo "No debug messages - hooks may not have triggered.";
}
echo '</pre>';
}, 999);
}

我们可以给它再加个简单的CSS,让访问一眼就知道哪些是“置顶”的。
.is-sticky-post {
position: relative;
border: 2px solid #ff6b6b;
}
.is-sticky-post::before {
content: "";
position: absolute;
top: 10px;
right: 10px;
width: 28px;
height: 28px;
background-color: #ff6b6b;
/* 图钉 SVG(旋转45度的钉子) */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z'/%3E%3C/svg%3E");
background-size: 18px 18px;
background-position: center;
background-repeat: no-repeat;
border-radius: 50%;
z-index: 10;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

