通过 js 让元素失去焦点
 · 阅读需 3 分钟
今天做项目,遇到一个需求。点击输入框获取焦点,按下回车后,执行搜索,失去焦点。
为什么会有这样的需求
以下内容,不考虑部分输入法等兼容性问题
项目大致要求是这样子的:
- 页面提供搜索框,同时提供搜索历史方便直接选择;
- 输入内容后,按回车,自动进行搜索,结果显示在下方区域。同时历史记录隐藏;
- 再次进行输入内容时候,搜索结果移除,继续显示搜索记录提供方便选择。
问题点
梳理需求后,其实可以简单理解整个流程,用一个状态进行表示:
搜索框获得焦点 => 显示历史记录
搜索框失去焦点 => 显示搜索结果
这个状态改变的规则为:
搜索框输入完毕(回车) => 搜索框失去焦点
搜索框被点击 => 搜索框获得焦点
第一版代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>test</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            font-size: 16px;
        }
        h1 {
            font-size: 18px;
        }
        .wrapper {
            margin: 8px;
        }
        .search {
            width: 100%;
            border: 0;
            border-bottom: 1px solid #333;
            outline: none;
        }
    </style>
</head>
<body>
<div class="wrapper">
    <h1>搜索:</h1>
    <input class="search" id="J_Search" type="search" autocomplete="off">
</div>
<div class="wrapper" id="J_History">
    <h1>搜索历史:</h1>
    <div>历史 bala bala...</div>
</div>
<div class="wrapper" id="J_Result">
    <h1>搜索结果:</h1>
    <div>结果 bala bala...</div>
</div>
<script>
    let $search = document.querySelector('#J_Search')
    let $history = document.querySelector('#J_History')
    let $result = document.querySelector('#J_Result')
    // 搜索框数据提交
    const searchSubmit = word => {
        $history.style.display = 'none'
        $result.style.display = 'block'
    }
    // 搜索框获得焦点
    $search.addEventListener('focus', () => {
        $history.style.display = 'block'
        $result.style.display = 'none'
    })
    // 搜索框输入
    $search.addEventListener('keyup', e => {
        if (e.keyCode === 13) {
            // 判断按回车键
            searchSubmit(e.target.value)
        }
    })
    // 默认显示形态
    $search.focus()
</script>
</body>
</html>
整体逻辑没问题,唯独就是,输入完毕提交后,输入框不会失去焦点,也就无法通过点击再次出发 focus 事件了。
第二版调整
考虑到要让输入框失去焦点,采用增加一个元素,并让其获得焦点即可。
但是增加的元素要有一些要求:
- 这个元素能获得焦点,那就得是控件元素
- 这个元素必须在页面内,还必须得显示,否则不能获得焦点
综合这两点,我选择 input-radio 控件,比较小巧不明显,而且不会触发移动端的输入法,同时,只能定位到屏幕外侧。
// new
// 失去焦点元素
const createBlurElment = () => {
  let $input = document.createElement('input')
  $input.type = 'radio'
  $input.style.position = 'absolute'
  $input.style.top = '-1000px'
  $input.style.height = '0'
  document.body.appendChild($input)
  return $input
}
let $search = document.querySelector('#J_Search')
let $history = document.querySelector('#J_History')
let $result = document.querySelector('#J_Result')
// new
let $blurElement = createBlurElment()
// 搜索框数据提交
const searchSubmit = word => {
  $history.style.display = 'none'
  $result.style.display = 'block'
}
// 搜索框获得焦点
$search.addEventListener('focus', () => {
  $history.style.display = 'block'
  $result.style.display = 'none'
})
// 搜索框输入
$search.addEventListener('keyup', e => {
  if (e.keyCode === 13) {
    // 判断按回车键
    searchSubmit(e.target.value)
    // new
    $blurElement.focus()
  }
})
// 默认显示形态
$search.focus()
这样就曲线救国,通过 js 让指定元素失去焦点了。