EP085. “无 JS 搜索页面与表单”
🔒 登录后可标记已读前面几讲做的搜索 Overlay 依赖 JavaScript,这一讲开始给「JavaScript 被禁用/加载失败」的少数访客做一个兜底方案:新建一个 /search 页面放传统的 HTML 搜索表单,表单直接用 GET 方式提交到网站根目录并带上 WordPress 原生识别的 s 参数(WordPress 传统搜索就是靠 ?s=关键词 这个 URL 格式驱动的)。同时把顶部搜索图标从 <span> 改成指向 /search 的 <a> 链接,让没有 JS 的访客点击后能正常跳转,而有 JS 的访客点击时用 return false 拦截默认跳转行为,继续用回原本的 Overlay。
涉及文件
- Pages 后台新建页面「Search」(slug:
search) wp-content/themes/fictional-university-theme/page-search.php(新建)wp-content/themes/fictional-university-theme/header.php(修改)wp-content/themes/fictional-university-theme/src/modules/Search.js(修改)
代码实现
page-search.php(新建,复制自 page.php 再把正文替换成搜索表单):
<?php
get_header();
while(have_posts()) {
the_post();
pageBanner();
?>
<div class="container container--narrow page-section">
<?php
$theParent = wp_get_post_parent_id(get_the_ID());
if ($theParent) { ?>
<div class="metabox metabox--position-up metabox--with-home-link">
<p><a class="metabox__blog-home-link" href="<?php echo get_permalink($theParent); ?>"><i class="fa fa-home" aria-hidden="true"></i> Back to <?php echo get_the_title($theParent); ?></a> <span class="metabox__main"><?php the_title(); ?></span></p>
</div>
<?php }
?>
<?php
$testArray = get_pages(array(
'child_of' => get_the_ID()
));
if ($theParent or $testArray) { ?>
<div class="page-links">
<h2 class="page-links__title"><a href="<?php echo get_permalink($theParent); ?>"><?php echo get_the_title($theParent); ?></a></h2>
<ul class="min-list">
<?php
if ($theParent) {
$findChildrenOf = $theParent;
} else {
$findChildrenOf = get_the_ID();
}
wp_list_pages(array(
'title_li' => NULL,
'child_of' => $findChildrenOf,
'sort_column' => 'menu_order'
));
?>
</ul>
</div>
<?php } ?>
<!-- 新增:用表单代替原本的 the_content() -->
<div class="generic-content">
<form class="search-form" method="get" action="<?php echo esc_url(site_url('/')); ?>">
<label class="headline headline--medium" for="s">Perform a New Search:</label>
<div class="search-form-row">
<input placeholder="What are you looking for?" class="s" id="s" type="search" name="s">
<input class="search-submit" type="submit" value="Search">
</div>
</form>
</div>
</div>
<?php }
get_footer();
?>
header.php:把两处搜索图标从 <span> 换成 <a>(桌面版和移动版各一处):
<!-- 修改前 -->
<span class="js-search-trigger site-header__search-trigger"><i class="fa fa-search" aria-hidden="true"></i></span>
...
<span class="search-trigger js-search-trigger"><i class="fa fa-search" aria-hidden="true"></i></span>
<!-- 修改后 -->
<a href="<?php echo esc_url(site_url('/search')); ?>" class="js-search-trigger site-header__search-trigger"><i class="fa fa-search" aria-hidden="true"></i></a>
...
<a href="<?php echo esc_url(site_url('/search')); ?>" class="search-trigger js-search-trigger"><i class="fa fa-search" aria-hidden="true"></i></a>
src/modules/Search.js:openOverlay() 末尾加 return false,阻止链接默认跳转行为(有 JS 时点击图标只弹出 Overlay,不会真的跳去 /search):
openOverlay() {
this.searchOverlay.classList.add("search-overlay--active")
document.body.classList.add("body-no-scroll")
this.searchField.value = ""
setTimeout(() => this.searchField.focus(), 301)
this.isOverlayOpen = true
return false // 新增
}
关键改动点:
- 表单的
action用esc_url(site_url('/'))而不是让浏览器默认提交到当前页面 URL——这样不管表单放在哪个页面,提交后都会统一带着?s=关键词跳到网站根目录,这正是 WordPress 原生识别搜索请求的 URL 格式 - 搜索输入框的
name属性必须是小写s,这是 WordPress 约定的搜索参数名,改成别的名字 WordPress 不会认 - 表单
method显式写get(虽然大多数浏览器默认就是 get,但明确写出来更保险),这样搜索词会体现在 URL 里,方便直接分享/加书签 esc_url()包住任何从数据库/配置里输出到 HTML 属性的 URL,是 WordPress 的安全惯例,用来防止被篡改的 URL 值注入恶意代码;这次是为了保护访客,不是为了防止网站本身被黑<label for="s">配合<input id="s">:点击文字标签会自动聚焦到输入框,这是无障碍访问的基本做法
[截图:前台 /search 页面实际效果,标题为 "Search Results" 的搜索表单]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
site_url($path) | WP 内建 function | 生成指向网站根目录(或指定路径)的完整 URL |
esc_url() | WP 内建 function | 安全转义 URL,输出到 HTML 属性前的标准做法 |
常见坑
- 表单的
action忘记指向网站根目录,直接用默认提交行为——如果表单放在别的页面下,提交后 URL 会变成/该页面/?s=xxx而不是标准的 WordPress 搜索 URL,虽然依然能查出结果,但不是 WordPress 推荐的标准搜索入口格式 openOverlay()忘记加return false——不加的话,即使 JS 正常工作,点击搜索图标除了弹出 Overlay,浏览器还是会继续执行链接的默认跳转行为,直接把用户带去/search
延伸 / 后续讲座会用到
下一讲(EP086)会创建 search.php 主题模板,让搜索结果页脱离默认的 index.php(博客列表)样式,按文章类型分别输出不同格式的结果卡片。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 17, EP085