Bootstrap Paper 的单选/复选框输入材质样式在 IE/FF 中损坏

Kyl*_*Mit 2 css html-input twitter-bootstrap material-design bootstrap-themes

根据Google选择控件(复选框、单选按钮、开关)的材料指南,这些输入字段应如下所示:

材料选择输入

v3.x 的 Bootstrap Paper 主题(现在在 v4 中称为 Materia)的样式旨在达到此设计目标

在 Chrome 中,输入的风格化是正确的,但所有其他浏览器只是回退到本机输入控件,这破坏了设计隐喻的一致性。

这是jsFiddle / Stack Snippets中的 MCVE ,它的工作/失败取决于浏览器:

<link href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/paper/bootstrap.min.css" rel="stylesheet"/>


<div class="checkbox">
  <label><input type="checkbox" value="check1"/> Check Opt 1 </label>
</div>
<div class="checkbox">
  <label><input type="checkbox" value="check2" checked/> Check Opt 2</label>
</div>

<div class="radio">
  <label><input type="radio" value="radio1" name="grp1" />Radio Opt 1</label>
</div>
<div class="radio">
  <label><input type="radio" value="radio2" name="grp1" checked/>Radio Opt 2</label>
</div>
Run Code Online (Sandbox Code Playgroud)

前面的代码在每个浏览器中的表现如下:

浏览器兼容性

是否有可能的解决方法,或者我们是否受浏览器的摆布,以他们认为合适的方式设置输入样式?

这个问题也在github 问题 #497 的项目问题页面上提出,但因浏览器不兼容而关闭

Kyl*_*Mit 5

实现 Paper 主题面临的核心问题是它是通过<input type="checkbox" />使用伪元素:before对元素进行样式化来实现的。:after

\n\n

根据这个W3C 规范和这个答案,我可以在输入字段上使用伪元素吗?

\n\n
\n

:before:after在容器内渲染,并且<input>s 不能包含元素

\n
\n\n

事实上,它在 Chrome 中工作是例外,而不是规则,但我们并不是运气不好......

\n\n

纯 CSS 方法

\n\n

所以我们要避免在其中设置任何样式实际复选框本身内设置任何样式,但我们可以将这些相同的样式应用于标签,这仍然会切换它所描述的复选框。

\n\n

第 1 步- 基于标签的切换

\n\n

我们确实必须重构 HTML,以便复选框位于标签之前,而不是标签内部。我们将这样做,以便我们可以使用:checked和 相邻同级选择器+根据是否选中复选框来有条件地设置标签样式。

\n\n
<!-- Old Format -->\n<div class="checkbox">\n  <label>\n    <input type="checkbox" value="check1"/>\n    Check Label Text\n  </label>\n</div>\n\n<!-- New Format -->\n<div class="label-check">\n   <input type="checkbox" value="check1" id="myCheck">\n   <label for="myCheck">Check Label Text</label>\n</div>\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

注意:为了使此解决方案发挥作用,您需要确保每个输入都具有有效的 HTMLfor ,其中包含唯一的 ID 以及描述它们的标签中的相应属性。

\n
\n\n

此时,我们可以放弃输入,并使用我们自己的:before伪元素(看起来像复选框或单选按钮)设置标签样式。为了让事情简单起见,在第一步中,我们可以使用以下 Unicode 字符来模拟控件... \xe2\x98\x90\xe2\x98\x91\xe2\xad\x98\xe2\x97\ x89

\n\n

这是一个简单的版本,其中包含 CSS 的基本要点,用于有条件地设置每个标签的样式:

\n\n
input[type=\'checkbox\'],[type=\'radio\']     { display: none; }\n[type=\'checkbox\']         + label::before { content: "\\2610";}\n[type=\'checkbox\']:checked + label::before { content: "\\2611";}\n[type=\'radio\']            + label::before { content: "\\2B58";}\n[type=\'radio\']:checked    + label::before { content: "\\25C9";}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们还可以通过添加一些着色、悬停效果、光标属性、过渡效果和文本阴影来对该基线进行相当多的改进,以使 unicode 字符看起来和感觉起来像实际的按钮。

\n\n

这是jsFiddle和 Stack Snippets 中更充实的演示:

\n\n

\r\n
\r\n
body {\r\n  font-size:1.3em;\r\n  color: #2b2b2b;\r\n  background:white;\r\n}\r\n.label-check input {  display:none; }\r\n\r\n.label-check label::before {\r\n  width: 1.4em;\r\n  text-align: center;\r\n  display: inline-block;\r\n  cursor: pointer;\r\n  color: black;\r\n  transition: color .3s ease;\r\n  text-shadow: 0px 0px 1px #cccccc;\r\n}\r\n.label-check label:hover::before {\r\n  text-shadow: 0px 0px 1px #6286d0;\r\n}\r\n.label-check [type=\'checkbox\']:checked + label::before,\r\n.label-check [type=\'radio\']:checked    + label::before{ \r\n  color: #056dce;\r\n}\r\n\r\n.label-check [type=\'checkbox\']         + label::before { content: "\\2610";}\r\n.label-check [type=\'checkbox\']:checked + label::before { content: "\\2611";}\r\n.label-check [type=\'radio\']            + label::before { content: "\\2B58";}\r\n.label-check [type=\'radio\']:checked    + label::before { content: "\\25C9";}
Run Code Online (Sandbox Code Playgroud)\r\n
<h3>\r\n  Inputs... we don\'t need no stinkin Inputs\r\n</h3>\r\n\r\n<div class="label-check">\r\n  <div>\r\n    <input type="checkbox" name="checkGrp1" id="check1_Opt1">\r\n    <label for="check1_Opt1">A Label Here 1</label>\r\n  </div>\r\n  <div>\r\n    <input type="checkbox" name="checkGrp1" id="check1_Opt2" checked>\r\n    <label for="check1_Opt2">A Label Here 2</label>\r\n  </div>\r\n</div>\r\n\r\n<div class="label-check">\r\n  <div>\r\n    <input type="radio" name="radioGrp1" id="radio1_Opt1">\r\n    <label for="radio1_Opt1">Radio Label 1</label>\r\n  </div>\r\n  <div>\r\n    <input type="radio" name="radioGrp1" id="radio1_Opt2" checked>\r\n    <label for="radio1_Opt2">Radio Label 2</label>\r\n  </div>  \r\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

第 2 步label:before-使用 CSS应用材质样式

\n\n

通过使用标签的风格化伪元素作为游戏中的切换的概念,我们可以应用 Paper 的样式/CSS,而不仅仅是{ content: "\\2610";}为我们的复选框提供 Material 外观和感觉。

\n\n

为此,让我们看看样式应该如何工作。我们可以在Github上查看 bootstrap.css#L7182中描述检查和无线电的源代码

\n\n

:after元素用于设置周围框/圆的样式,而该:before元素在框为 时添加内部选择:checked

\n\n

以下是复选框的高级样式:

\n\n

材质复选框 CSS

\n\n
\n

注意:复选标记是通过将矩形旋转 45 度并在底部和右侧添加白色边框制成的。

\n
\n\n

这是单选按钮样式的高级视图

\n\n

材质无线电CSS

\n\n
\n

注意:单选按钮容器及其内部只是一个border-radius:50%大小不同的常规 CSS 圆圈 ( ),可以通过放大或缩小来实现动画效果。

\n
\n\n

因此,我们会将复选框输入中找到的任何伪元素迁移到其相邻标签,如下所示:

\n\n
/* old */ input[type="radio"]:before\n/* new */ input[type="radio"] + label:before\n
Run Code Online (Sandbox Code Playgroud)\n\n

经过一番仔细的转换,这是jsFiddle中的演示和 Stack Snippets 中的演示:

\n\n

\r\n
\r\n
body {\r\n  padding: 0 25px;\r\n  font-size: 1.2em;\r\n}\r\n\r\n\r\n.checkbox, .radio {\r\n    margin: 10px;\r\n}\r\n.checkbox, .radio {\r\n    position: relative;\r\n}\r\n\r\n\r\n.label-check label {\r\n  padding-left: 20px;\r\n}\r\n.label-check input[type="radio"],\r\n.label-check input[type="checkbox"] {\r\n  -webkit-appearance: none;\r\n     -moz-appearance: none;\r\n          appearance: none;\r\n    opacity: 0;\r\n    position: absolute;\r\n    margin: 0;\r\n    z-index: -1;\r\n    width: 0;\r\n    height: 0;\r\n    overflow: hidden;\r\n    left: 0;\r\n    pointer-events: none;\r\n}\r\n.label-check input[type="radio"]:focus {\r\n  outline: none;\r\n}\r\n.label-check input[type="radio"] + label:before,\r\n.label-check input[type="radio"] + label:after {\r\n  content: "";\r\n  display: block;\r\n  position: absolute;\r\n  left: -10px;\r\n  top: 1px;\r\n  width: 18px;\r\n  height: 18px;\r\n  border-radius: 50%;\r\n  -webkit-transition: 240ms;\r\n  -o-transition: 240ms;\r\n  transition: 240ms;\r\n}\r\n.label-check input[type="radio"] + label:before {\r\n    left: -8px;\r\n    top: 3px;\r\n}\r\n.label-check input[type="radio"] + label:before {\r\n  background-color: #2196f3;\r\n  -webkit-transform: scale(0);\r\n      -ms-transform: scale(0);\r\n       -o-transform: scale(0);\r\n          transform: scale(0);\r\n}\r\n.label-check input[type="radio"] + label:after{\r\n  top: 1px;\r\n  border: 2px solid #666666;\r\n  z-index:1;\r\n}\r\n.label-check input[type="radio"]:checked + label:before {\r\n  -webkit-transform: scale(0.6);\r\n      -ms-transform: scale(0.6);\r\n       -o-transform: scale(0.6);\r\n          transform: scale(0.6);\r\n}\r\n.label-check input[type="radio"]:disabled:checked + label:before {\r\n  background-color: #bbbbbb;\r\n}\r\n.label-check input[type="radio"]:checked + label:after {\r\n  border-color: #2196f3;\r\n}\r\n.label-check input[type="radio"]:disabled + label:after,\r\n.label-check  input[type="radio"]:disabled:checked + label:after {\r\n  border-color: #bbbbbb;\r\n}\r\n.label-check input[type="checkbox"]:focus {\r\n  outline: none;\r\n}\r\n.label-check input[type="checkbox"]:focus + label:after{\r\n  border-color: #2196f3;\r\n}\r\n.label-check input[type="checkbox"] + label:after {\r\n  content: "";\r\n  position: absolute;\r\n  top: 2px;\r\n  left: -10px; \r\n  display: block;\r\n  width: 18px;\r\n  height: 18px;\r\n  margin-top: -2px;\r\n  margin-right: 5px;\r\n  border: 2px solid #666666;\r\n  border-radius: 2px;\r\n  -webkit-transition: 240ms;\r\n       -o-transition: 240ms;\r\n          transition: 240ms;\r\n}\r\n.label-check input[type="checkbox"]:checked + label:before {\r\n  content: "";\r\n  position: absolute;\r\n  top: 2px;\r\n  left: -3px;\r\n  display: table;\r\n  width: 6px;\r\n  height: 12px;\r\n  border: 2px solid #fff;\r\n  border-top-width: 0;\r\n  border-left-width: 0;\r\n  -webkit-transform: rotate(45deg);\r\n      -ms-transform: rotate(45deg);\r\n       -o-transform: rotate(45deg);\r\n          transform: rotate(45deg);\r\n  z-index:1;\r\n}\r\n.label-check input[type="checkbox"]:checked + label:after{\r\n  background-color: #2196f3;\r\n  border-color: #2196f3;\r\n}\r\n.label-check input[type="checkbox"]:disabled + label:after {\r\n  border-color: #bbbbbb;\r\n}\r\n.label-check  input[type="checkbox"]:disabled:checked + label:after {\r\n  background-color: #bbbbbb;\r\n  border-color: transparent;\r\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<h3>\r\n Material Label Based Checks\r\n</h3>\r\n\r\n<div class="label-check">\r\n  <div class="checkbox">\r\n    <input type="checkbox" name="checkGrp1" id="check1_Opt1">\r\n    <label for="check1_Opt1">A Label Here 1</label>\r\n  </div>\r\n  <div class="checkbox">\r\n    <input type="checkbox" name="checkGrp1" id="check1_Opt2" checked>\r\n    <label for="check1_Opt2">A Label Here 2</label>\r\n  </div>\r\n</div>\r\n\r\n<div class="label-check">\r\n  <div class="radio">\r\n    <input type="radio" name="radioGrp1" id="radio1_Opt1">\r\n    <label for="radio1_Opt1">Radio Label 1</label>\r\n  </div>\r\n  <div class="radio">\r\n    <input type="radio" name="radioGrp1" id="radio1_Opt2" checked>\r\n    <label for="radio1_Opt2">Radio Label 2</label>\r\n  </div>  \r\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

HTML/JS 方法

\n\n

还有一些其他材料实现以跨浏览器友好的方式达到了这个目标。所有这些通常依赖于:

\n\n
    \n
  1. 隐藏实际<input>元素
  2. \n
  3. 在标签内放置一个程式化的复选框
  4. \n
\n\n

在某些情况下,这依赖于您在设计时适当地格式化 HTML,或者在站点初始化时生成它。

\n\n

例如,FezVrasta 的 Bootstrap Material DesignV3V4中都将采用如下 HTML 结构:

\n\n\n\n
<div class="checkbox">\n  <label>\n    <input type="checkbox"> \n    Notifications\n  </label>\n</div>\n
Run Code Online (Sandbox Code Playgroud)\n\n

并在调用时插入一个新的跨度,$.material.init()因此结果如下所示:

\n\n\n\n
<div class="checkbox">\n  <label>\n    <input type="checkbox">\n    <span class="checkbox-material">\n      <span class="check"></span>\n    </span>\n    Notifications\n  </label>\n</div>\n
Run Code Online (Sandbox Code Playgroud)\n\n

虽然这依赖于初始化的发生,但它允许对 CSS 复选框和单选按钮进行更细粒度的控制,而不是将其全部推入伪元素中。

\n