点击标签时,如何将类似滑块的复选框的滑块移动到标签上



我需要将开关滑块移动到用户单击的单词(中或大(。我不知道我是否可以用JavaScript跟踪开关滑块的移动,或者可能用css来跟踪。目前,我只创建了一个简单的切换开关与变化的颜色。问题是滑块被定制了很多:以前,:检查,输入等等,所以我甚至不知道从哪里开始。

.slider.round {
border-radius: 34px;
}

.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}

.switch input { 
opacity: 0;
width: 0;
height: 0;
}

.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}

.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}

input:checked + .slider {
background-color: #13985C;
}

input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>           
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>
</div>

让我们循序渐进:

  • 我们得到mediumlarge和输入元素
  • 向其中添加onclick事件监听器以进行切换
  • 添加cursor: pointer以显示它们可点击

var medium = document.querySelector('.medium'),
large = document.querySelector('.large'),
slider = document.querySelector('.switch input');;
medium.onclick = function() {
slider.checked = false;
}
large.onclick = function() {
slider.checked = true;
}
.medium, .large {
cursor: pointer;
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:checked+.slider {
background-color: #13985C;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size medium">
<bold>Medium</bold>
</div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size large">
<bold>Large</bold>
</div>
</div>
</div>

使用addEventListener()

通过在标签中添加一个事件侦听器来(取消(选中复选框,可以实现所需的行为。

我们可以使用事件委派只向父级添加一个事件侦听器。我们可以使用布尔值,该布尔值告诉我们点击的标签是否是;关于"-标签(意思是应该选中复选框的标签(
此布尔值可以立即用作.checked-属性现在应该具有的值。快速解释:

  • 标签是";关于"-标签如果是,请检查
  • 标签是";关于"-标签如果否,请执行而不是检查

在伪代码中,它看起来像这样:
checkbox.checked = isOnLabel

我们可以检查点击的标签是否是";关于"-标签,查看它所在的.row-input-div是否是最后一个。

注意:
只有在默认情况下不可点击的元素中添加点击侦听器才能访问该元素
要使元素可访问并可点击,必须:

  • 添加所有适当的侦听器(点击侦听器、键侦听器;特定元素侦听特定键,例如"空格"的按钮、"回车"的锚点、箭头键的单选组(
  • 使元素符合ARIA(使用语义上有意义的HTML,例如特定的HTML元素aria-rolearia-label(

这可能需要做很多工作,所以除非有充分的理由从new创建元素,否则应该坚持HTML规范中当前给出的内容(尽管在MDN上阅读HTML元素更容易(。

以下是显示结果的片段:
注意:该代码也适用于多个.column-inputs。

for (let ci of document.querySelectorAll('.column-input')) {
ci.addEventListener('click', evt => {
let ri = evt.target.closest('.row-input'); // Get the '.row-input'-div
if (ri.querySelector('.item-size')) { // If `ri` contains '.item-size' / a label, continue
// '!ri.nextElementSibling' is true if `ri` is the last element
ci.querySelector('input').checked = !ri.nextElementSibling;
}
});
}
.slider.round {border-radius: 34px}
.slider.round:before {border-radius: 50%}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {margin-left: 10px}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {outline: none}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input { 
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:focus + .slider {box-shadow: 0 0 1px #2196F3}
input:checked + .slider {background-color: #13985C}
input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>
</div>
</div>
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Mayo</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Ketchup</bold></div>
</div>
</div>
</div>

仅限HTML/CSS

还可以使用无线电组来制作类似滑块的元件,其中无线电组将由两个元件组成:;关于";以及";关闭";。

结合标签,用户(视觉或工具辅助(知道滑块的两个选项是什么,并可以通过使用鼠标或键盘相应地选择一个
你可以自己试试按Tab键进入单选组,然后按箭头键。

将标签与输入元素关联具有以下优点:

  • 它为工具(例如屏幕阅读器(正确地标记其输入元素
  • 单击标签的行为与单击其输入元素本身的行为相同
    (默认情况下,我们在使用addEventListener()之前获得的效果是针对关联标签实现的(
  • 它不需要任何JavaScript

对于图形,我没有滥用HTML元素,而是将SVG与CSS结合使用。

具有多个输入元素的优点是能够给每个输入元素一个单独的值。这对于提交<form>可能很有用,这样后端就更容易弄清楚这些值的含义(例如,无线电组的"Mayo"/"Ketchup"与复选框的"Mayo or Ketchup" = (true | false)(。这也使维护变得更容易,后端不必查看前端代码就能理解价值的含义。

请注意<svg><label>aria-hidden="true"-属性。这告诉浏览器在构建辅助功能树时忽略这些标签,这意味着只有带有标签内容的标签才会与其输入元素相关联。

据我所知,这应该是一种方式,可以让鼠标和键盘用户,以及视力和工具辅助用户完全访问您想要的滑块机制。

/* OPTIONAL
* 
* HTML-element can be used without JS.
* However, one would need to manually add
* the radio-names in these places:
* - as <input>'s names
* - prepend <input>'s id
* - prepend <label>'s for
*/
for (var rs of document.querySelectorAll('.radio-slider')) {
var rsName = rs.getAttribute('data-name') || 'broken';
for (var rsInput of rs.querySelectorAll('input')) {
rsInput.id = rsName + rsInput.id;
rsInput.name = rsName;
}
for (var rsLabel of rs.querySelectorAll('label')) {
rsLabel.setAttribute('for', rsName + rsLabel.getAttribute('for'));
}
}
/* Tool-classes (TC); DO NOT CHANGE */
/* TC: '.sr-only' */
.sr-only {
position: absolute;
margin: -1px;
padding: 0;
clip: rect(0,0,0,0);
width: 1px;
height: 1px;
border: 0;
overflow: hidden;
}
/* TC: '.radio-slider' */
.radio-slider svg { /* Dimension of 8:5 */
width: 2rem;
height: 1.25rem;
}
.radio-slider > div {display: flex}
.radio-slider input:focus-visible ~ div {outline: auto}
.radio-slider input:focus-visible ~ div svg rect {fill: #2196F3}
.radio-slider circle,
.radio-slider rect {transition: 0.5s}
.radio-slider foreignObject > label {
width: 100%;
height: 18px;
display: block;
}
.radio-slider .on:checked ~ div svg foreignObject:last-child {display: none}
.radio-slider .on:checked ~ div svg rect {fill: #13985C}
.radio-slider .on:checked ~ div svg circle {transform: translateX(calc(2rem - 16px))}
<div class="radio-slider" data-name="radio1">
<input id="-off" class="sr-only off" type="radio">
<input id="-on" class="sr-only on" type="radio">
<div>
<label for="-off">Medium</label> 
<svg viewBox="0 0 32 20" xmlns="xmlns=http://www.w3.org/1999/xhtml" stroke="black" fill="lightgray" stroke-width="0.5">
<rect x="4" y="4" rx="4" width="24" height="8" />
<circle cx="8" cy="8" r="6" fill="white" />
<foreignObject width="100%" height="100%"><label for="-off" aria-hidden="true"></label></foreignObject>
<foreignObject width="100%" height="100%"><label for="-on" aria-hidden="true"></label></foreignObject>
</svg>
<label for="-on">Large</label>
</div>
</div>
<div class="radio-slider" data-name="radio2">
<input id="-off" class="sr-only off" type="radio">
<input id="-on" class="sr-only on" type="radio">
<div>
<label for="-off">Mayo</label> 
<svg viewBox="0 0 32 20" xmlns="xmlns=http://www.w3.org/1999/xhtml" stroke="black" fill="lightgray" stroke-width="0.5">
<rect x="4" y="4" rx="4" width="24" height="8" />
<circle cx="8" cy="8" r="6" fill="white" />
<foreignObject width="100%" height="100%"><label for="-off"></label></foreignObject>
<foreignObject width="100%" height="100%"><label for="-on"></label></foreignObject>
</svg>
<label for="-on">Ketchup</label>
</div>
</div>

在我的例子中,我没有为每一侧分配一个唯一的类。因此,我使用了forEach()方法。

let select = document.querySelectorAll('.row-input .item-size');
let checked_pos = document.querySelector('.row-input .switch input[type="checkbox"]');
Array.from(select).forEach(function(selectCurrent, index) {
selectCurrent.onclick = function() {
if (index == 0) {
checked_pos.checked = false;
}
if (index == 1) {
checked_pos.checked = true;
}
}
});
.slider.round {
border-radius: 34px;
}

.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}

.switch input { 
opacity: 0;
width: 0;
height: 0;
}

.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}

.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}

input:checked + .slider {
background-color: #13985C;
}

input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">

<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>       
</div>

<div class="row-input">
<label class="switch">
<input type="checkbox" checked/>
<span class="slider round"></span>
</label>
</div>

<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>

</div>

最新更新