focus-within封装组件必不可少的css伪类
从一开始还真没怎么关注到这个属性,直到最近在封装自己的组件库,在使用过程中有很多困扰我的地方。比如我封装一个按钮或者checkbox这种组件,在点击的时候给上一个状态,再点击其余地方的时候这个状态取消,第一想法我就是用input这个标签有方法,获取焦点focus,blur用这个不就可以完美的通过控制class然后来实现效果了吗,这个方法确实可以一开始我也是这么做的,但是组件库越来越多的时候我就觉得很麻烦了,每个组件我都需要去添加很多state显示classname,工作量就越来越大,代码也不简洁看起来很重。
每个组件需要这种焦点的我都是这样写的是不是觉得很重,这个地方不是我写焦点的地方只是差不多的实现。
<input
ref={actionRef}
placeholder={placeholder}
type={inputType}
value={text}
onChange={(e) => {
e.target.value = e.target.value.substring(
0,
maxLength ? maxLength : e.target.value.length,
);
setTextSize(e.target.value.length);
setText(e.target.value);
onChange?.(actionRef);
}}
onBlur={(e) => {
if (disabled) return;
setIsClass(false);
onBlur?.(e);
}}
onFocus={() => {
setIsClass(true);
}}
onKeyDown={(e) => {
if (e.keyCode === 13) {
props.onPressEnter?.();
}
}}
disabled={disabled}
/>
偶然发现focus-within
太香了,只要是Input获取了焦点不管是父级多少只要使用了这个伪类,子集input只要获取了焦点,父节点也可以使用这个伪类来写一些焦点的样式,这样就大大节省了我组件库代码量了省去了很多的变量。
当用户在一个表单中输入时,我们可以使用传统方式 来改变父级元素的样式,以提供焦点可见性和用户反馈。
<style>
.form {
margin-bottom: 20px;
}
.form.with-focus {
background-color: lightblue;
border: 2px solid blue;
}
</style>
<div class="form">
<label for="name">姓名:</label>
<input type="text" id="name" placeholder="请输入姓名">
</div>
<div class="form">
<label for="email">邮箱:</label>
<input type="email" id="email" placeholder="请输入邮箱">
</div>
<div class="form">
<label for="message">留言:</label>
<textarea id="message" placeholder="请输入留言"></textarea>
</div>
<script>
const forms = document.querySelectorAll('.form');
forms.forEach((form) => {
const input = form.querySelector('input, textarea');
input.addEventListener('focus', () => {
form.classList.add('with-focus');
});
input.addEventListener('blur', () => {
form.classList.remove('with-focus');
});
});
</script>
在上述例子中,每个表单项包含一个 <label>
元素和一个 <input>
或 <textarea>
元素。通过 JavaScript,我们监听每个表单项内部输入框的 focus
和 blur
事件,并在焦点获得时为父级的 .form
添加 with-focus
类,使其应用样式变化。当焦点失去时,移除该类,恢复默认样式。
下面是一个使用 focus-within
的例子:
<style>
.form {
margin-bottom: 20px;
}
.form:focus-within {
background-color: lightblue;
border: 2px solid blue;
}
</style>
<div class="form">
<label for="name">姓名:</label>
<input type="text" id="name" placeholder="请输入姓名">
</div>
<div class="form">
<label for="email">邮箱:</label>
<input type="email" id="email" placeholder="请输入邮箱">
</div>
<div class="form">
<label for="message">留言:</label>
<textarea id="message" placeholder="请输入留言"></textarea>
</div>
<script>
const forms = document.querySelectorAll('.form');
forms.forEach((form) => {
const input = form.querySelector('input, textarea');
});
</script>
使用伪类很轻松就能实现刚刚的功能。不需要再去维护其余的变量和多余的classname。