首页 > 编程笔记 > CSS笔记

CSS :required和:optional的用法

:required 伪类用来匹配设置了 required 属性的表单元素,表示这个表单元素必填或者必写。

例如:
<input required>
<select required>
   <option value="">请选择</option>
   <option value="1">选项1</option>
   <option value="2">选项2</option>
</select>
<input type="radio" required>
<input type="checkbox" required>
对于以上 4 个表单元素,:required 伪类均可以匹配。例如:
:required {
   box-shadow: 0 0 0 2px green;
}
结果都呈现为绿色的线框,如图 1 所示。


图 1 :required伪类匹配示意

:optional 伪类可以看成 :required 伪类的对立面,即只要表单元素没有设置 required 属性,:optional 伪类都可以匹配,甚至对于 <button> 按钮也可以匹配。例如:
:optional {
   box-shadow: 0 0 0 2px red;
}
<button>按钮</button>
<input type="submit" value="按钮">
这两种写法的按钮元素都呈现为红色的线框,如下图所示。


图 2 :optional伪类匹配示意

值得一提的是单选框元素的 :required 伪类匹配。虽然单选框元素的 :required 伪类匹配和 :invalid 伪类匹配的机制有巨大差异,但很多人会误认为它们是一样的。

对于 :invalid 伪类,只要其中一个单选框元素设置了 required 属性,:invalid 伪类就会匹配整个单选框组中的所有单选框元素,导致同时验证通过或验证不通过;但是,如果是 :required 伪类,则只会匹配设置了 required 属性的单选框元素。例如:
[type="radio"]:required {
   box-shadow: 0 0 0 2px deepskyblue;
}
[type="radio"]:invalid {
   outline: 2px dashed red;
   outline-offset: 4px;
}
<input type="radio" name="required" required>
<input type="radio" name="required">
<input type="radio" name="required">
<input type="radio" name="required">
结果第一个设置了 required 属性的单选框元素有两层轮廓,其他只有 :invalid 伪类匹配的单选框元素只有一层轮廓,如下图所示。


图 3 :required伪类和:invalid伪类匹配单选框组的差异

实际应用

长久以来,输入框是必选还是可选,除了禁用状态,在样式上没有区别。我们通常的做法都是使用额外的字符进行标记。例如使用红色星号标记该输入框是必选的,或者直接使用中文“可选”来标记这个输入框是可以不填的。

因此实际开发中,:required 伪类和 :optional 伪类都是通过兄弟选择符控制兄弟元素的样式来标记表单元素的可选性的。

例如,图 4 所示的就是一个调查问卷布局的最终实现效果,可以看到每个问题的标题的最后都标记了“必选”或者“可选”,这些标记的文案是 CSS 根据 HTML 表单元素设置的属性自动生成的。


图 4 纯CSS标记“必选”或者“可选”示例

相关实现颇有技术含量,大家需要耐心查看代码,可以学到很多其他 CSS 技术。

首先是 HTML 代码部分,和传统实现不同,我们需要把标题元素放在表单元素的后面,这样才能使用兄弟选择符进行控制,具体如下:
<form>
   <fieldset>
      <legend>问卷调查</legend>
      <ol class="cs-ques-ul">
         <li class="cs-ques-li">
           <input type="radio" name="ques1" required>1-3年
           <input type="radio" name="ques1">3-5年
           <input type="radio" name="ques1">5年以上
           <!-- 标题放在后面 -->
           <h4 class="cs-caption">你从事前端几年了?</h4>
        </li>
        ...
        <li class="cs-ques-li">
            <textarea></textarea>
            <!-- 标题放在后面 -->
            <h4 class="cs-caption">有什么其他想说的?</h4>
         </li>
      </ol>
      <p><input type="submit" value="提交"></p>
   </fieldset>
</form>
接下来,高能的 CSS 来了,考验布局能力的时候到了,如何让后面的 .cs-caption 元素在上方显示呢?由于这里标签受限,因此使用 Flex 布局有些困难。实际上有一个 IE8 浏览器也支持的 CSS 声明可以改变 DOM 元素呈现的上下位置,这个 CSS 声明就是 display:table-caption,CSS 代码如下:
.cs-ques-li {
   display: table;
   width: 100%;
}
.cs-caption {
   display: table-caption;
   /* 标题显示在上方 */
   caption-side: top;
}
由于 <li> 元素设置了 display:table,重置了浏览器内置的 display:list-item,因此,列表前面的数字序号无法显示,但没关系,我们可以借助 CSS 计数器重现序号匹配,这也是从 IE8 浏览器就开始支持的,代码如下:
.cs-ques-ul {
   counter-reset: quesIndex;
}
.cs-ques-li::before {
   counter-increment: quesIndex;
   content: counter(quesIndex) ".";
   /* 序号定位 */
   position: absolute; top: -.75em;
   margin: 0 0 0 -20px;
}
最后就很简单了,基于 :optional 伪类和 :required 伪类在 .cs-caption 元素最后标记可选性。CSS 代码如下:
:optional ~ .cs-caption::after {
   content: "(可选)";
   color: gray;
}
:required ~ .cs-caption::after {
   content: "(必选)";
   color: red;
}
可见,借助以上 3 个 CSS 高级技巧实现了我们的可选性自动标记效果,以后要想修改可选性,只需要修改表单元素的 required 属性,文案信息会自动同步,维护更简单。

完整的 CSS 代码如下:
/* 标题在上方显示 */
.cs-ques-li {
   display: table;
   width: 100%;
}
.cs-caption {
   display: table-caption;
   caption-side: top;
}
/* 自定义列表序号 */
.cs-ques-ul {
   counter-reset: quesIndex;
}
.cs-ques-li {
   position: relative;
}
.cs-ques-li::before {
   counter-increment: quesIndex;
   content: counter(quesIndex) ".";
   position: absolute; top: -.75em;
   margin: 0 0 0 -20px;
}
/* 显示对应的可选性文案与颜色 */
:optional ~ .cs-caption::after {
   content: "(可选)";
   color: gray;
}
:required ~ .cs-caption::after {
   content: "(必选)";
   color: red;
}

推荐阅读