工作

给 WordPress 加一个能用的搜索框:从思路到上线

需求很小:网站要有个搜索。做下来发现,难点不在“能不能搜”,而在“搜的体验顺不顺”。

起点:为什么 WordPress 自带的搜索不行

WordPress 本身是有搜索的,但你得自己放一个入口。最省事的做法是用 get_search_form() 塞进页头——结果是:输入框常年占着导航栏的位置,要么丑,要么在旁边挂一个突兀的按钮。

我想要的效果是:

  • 桌面端不占地方,点放大镜才展开
  • 手机端一眼可见,别藏在二级菜单
  • 只搜文章,不要把“关于”这类页面和附件也搜出来
  • 输入框和整个站点的视觉风格一致

方案:把表单挂进导航菜单

WordPress 的 wp_nav_menu_items 过滤器可以在菜单末尾追加任意 HTML。这一点很关键——如果表单挂在导航外面,手机上菜单折叠时它不会跟着走。

add_filter('wp_nav_menu_items', function ($items, $args) {
    if (empty($args->theme_location) || $args->theme_location !== 'primary') {
        return $items;
    }
    $id = 'mohenji-search-' . wp_unique_id();
    return $items
        . '<li class="menu-item mohenji-search">'
        . '<form role="search" method="get" action="' . esc_url(home_url('/')) . '">'
        . '<label for="' . esc_attr($id) . '">搜索</label>'
        . '<input type="search" id="' . esc_attr($id) . '" name="s" placeholder="搜索文章…">'
        . '<button type="submit" aria-label="搜索">…</button>'
        . '</form></li>';
}, 20, 2);

wp_unique_id() 用来生成唯一 ID——如果页面上有两个搜索框(比如空结果页还有一个),label for 就不会指向错的那个。

只搜文章

默认搜索会把所有 post_type 都扫一遍。加三行限制,范围立刻收窄:

add_action('pre_get_posts', function ($query) {
    if (is_admin() || !$query->is_main_query() || !$query->is_search()) {
        return;
    }
    $query->set('post_type', 'post');
});

注意必须先判断 is_main_query(),否则会影响后台的查询。

交互:桌面展开,手机常驻

这里不需要框架,30 行原生 JS 就够:

var box = document.querySelector('.mohenji-search');
var toggle = box.querySelector('label');
var input = box.querySelector('input');

toggle.addEventListener('click', function (e) {
    e.preventDefault();
    if (box.classList.contains('is-open')) {
        input.value.trim() ? box.querySelector('form').submit() : box.classList.remove('is-open');
        return;
    }
    box.classList.add('is-open');
    input.focus();
});

// Esc 收起
document.addEventListener('keydown', function (e) {
    if (e.key === 'Escape') { box.classList.remove('is-open'); toggle.focus(); }
});

CSS 用 width 过渡实现展开动画,用 @media 让手机端固定为全宽:

.mohenji-search input { width: 0; opacity: 0; transition: width .28s; }
.mohenji-search.is-open input { width: 170px; opacity: 1; }

@media (max-width: 768px) {
    .mohenji-search label { display: none; }
    .mohenji-search input { width: 100%; opacity: 1; }
}

踩到的三个坑

白板上的流程设计
做组件之前先想清楚它在页面里占什么位置

坑一:输入框有个去不掉的内边框

展开后输入框里总有一圈灰色方框。排查发现是主题里更早定义的 input[type="search"] 样式在起作用,而我写的选择器优先级不够。

解决方式不是疯狂加 !important,而是提高选择器特异性:

.mohenji-search .mohenji-search-input { border: 0; outline: 0; }

同时还要关掉 Safari/Chrome 给 type="search" 加的默认清除按钮:

.mohenji-search-input::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }

坑二:空结果页的搜索表单不受控

用户搜不到东西时,WordPress 会自动渲染一个 get_search_form()。那个表单是主题的默认样式——方角、灰色按钮,跟整站格格不入。

处理思路是不改模板,只加样式:

.search-results .no-results .search-field {
    border-radius: 999px;
    border: 1.5px solid var(--line-strong);
}
.search-results .no-results .search-submit {
    background: var(--sky);
    border-radius: 999px;
}

坑三:装饰插图被卡片裁掉了

我想在空结果页放一张插图增加亲和力。第一次用 position: absolute 放在卡片左上角,结果整个被 overflow: hidden 裁掉了——因为卡片内边距只有 30px,而插图有 74px 高。

试了三种方案:

  • 绝对定位 + 内边距 → 内容被挤窄
  • 作为卡片第一行(display: block)→ 标题的负外边距把它顶出去了
  • 最终方案:放在卡片内部右上角,给右侧内容留 max-width 限制
.mohenji-no-results .no-results::after {
    content: "";
    position: absolute;
    top: 30px; right: 32px;
    width: 104px; height: 74px;
    background: var(--empty-art) center/contain no-repeat;
}

效果

上线后:

  • 桌面端点放大镜 → 输入框从右侧滑出 → 按回车搜索
  • 手机端菜单展开后,搜索框固定在底部,44px 高,单手可点
  • 结果页只显示文章卡片,关键词高亮
  • 空结果页有插图和可用的输入框

一点体会

这类小功能最容易翻车的地方,不是逻辑,而是样式冲突。WordPress 主题通常有几十条针对 input 的全局规则,你的组件只要有一个选择器没压住,就会出现奇怪的边框或背景。

我的做法是:写组件样式时,全部带上组件的命名空间(.mohenji-search .xxx),不要裸写 input。这样以后加新组件也不会互相打架。

发表评论