点击不同的单选按钮后取消选中之前的单选按钮

3
我正在制作一个投票系统,其中包括两个按钮(赞成/反对)。我使用了输入和标签标记来创建它,但是存在一个问题:
如果我使用单选按钮,我只能投赞成或反对票,但我无法取消投票。
如果我使用复选框,我可以取消投票,但也可以同时投赞成和反对票。
我的目标是只能投赞成或反对票,同时也能取消投票(类似Reddit)。
我已经彻底查找了答案,并发现这在CSS中是不可能实现的。虽然有很多脚本,但问题是我不知道JavaScript或jQuery,因此我不知道如何实现代码。
我认为最简单的解决方案是在点击赞成或反对按钮时取消另一个按钮的选择,反之亦然,但同样,我不知道这段代码应该是什么样子,也不知道如何实现它。
也许使用纯CSS有更简单的解决方案,即使已经被选中,也可以更改其他按钮的外观?我不知道,但如果存在这样的解决方案,我宁愿使用它。
我的代码:

input[type=radio] {
  display: none
}
#up {
  width: 20px;
  height: 16px;
  background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png) left top;
  position: absolute;
  left: 50px;
  top: 50px
}
#down {
  width: 20px;
  height: 16px;
  background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png) left bottom;
  opacity: 0.5;
  position: absolute;
  left: 50px;
  top: 84px
}
#vote {
  width: 21px;
  height: 18px;
  text-align: center;
  font-size: 26px;
  line-height: 18px;
  position: absolute;
  left: 50px;
  top: 66px;
  z-index: -1
}
#up:hover {
  background-position: top;
  cursor: pointer
}
#upvote:checked ~ #up {
  background-position: top;
}
#upvote:checked ~ #vote {
  color: #DC5B28;
  z-index: 1
}
#down:hover {
  background-position: bottom;
  cursor: pointer;
  opacity: 1
}
#downvote:checked ~ #down {
  background-position: bottom;
  opacity: 1
}
#downvote:checked ~ #vote {
  color: #3580DD
}
#no {
  position: absolute;
  display: none;
  background: url(http://c.thumbs.redditmedia.com/Y5Gt7Gtk59BlV-3t.png)
}
#upvote:checked ~ #no {
  display: block;
  background-position: left top;
  left: 50px;
  top: 50px
}
#downvote:checked ~ #no {
  display: block;
  background-position: left bottom;
  left: 50px;
  top: 84px;
  opacity: 0.5
}
<input type="radio" name="vote" value="+1" id="upvote">
<label for="upvote" id="up"></label>
<input type="radio" name="vote" value="-1" id="downvote">
<label for="downvote" id="down"></label>
<input type="radio" name="vote" value="0" id="novote">
<label for="novote" id="no"></label>
<div id="vote"></div>


请展示您尝试过的代码或正在使用的代码。即使您不了解JavaScript,也请至少向我们展示您的HTML,以便我们了解其外观。 - Black Bird
@BlackBird 是的,我的错,应该事先包含它。 - SoKeT
检查StackOverflow投票按钮的源代码 :) - rvighne
2个回答

6
如果你想要一个仅使用CSS的解决方案,可以尝试添加一个默认隐藏的第三个选项,表示没有投票。当用户进行赞成或反对的操作时,这个第三个选项会显示,并覆盖所选的选项。因此,当用户认为再次单击所选的选项时,实际上是选择了“无投票”选项。

#vote {
  position: relative;
}
#vote > input {
  display: none;                    /* Hide radio buttons */
}
#vote > label {
  cursor: pointer;
  display: block;
  width: 0;
  border: 50px solid;               /* We will make them look like... */
  border-color: black transparent;  /* ...triangles using borders */
}
#upvote + label {                   
  border-top: none;                 /* Triangulating */
}
#downvote + label {                 
  border-bottom: none;              /* Triangulating */
  margin-top: 15px;                 /* Space between triangles */
}
#vote > input:checked + label {     
  border-color: orange transparent; /* Highlight chosen option */
}
#vote > #novote-label { 
  display: none;                    /* Hide it by default */
  position: absolute;               /* Take it out of the normal flow */
  border-top: none;
  border-color: transparent;
}
#upvote:checked ~ #novote-label {   /* Display it overlapping upvote */
  display: block;
  top: 0;
}
#downvote:checked ~ #novote-label { /* Display it overlapping downvote */
  display: block;
  bottom: 0;
}
<div id="vote">
  <input type="radio" name="vote" value="+1" id="upvote" />
  <label for="upvote"></label>
  <input type="radio" name="vote" value="-1" id="downvote" />
  <label for="downvote"></label>
  <input type="radio" name="vote" value="0" id="novote" />
  <label for="novote" id="novote-label"></label>
</div>

只需进行少量更改,它也可以支持键盘访问:

#vote {
  position: relative;
}
#vote > input {                      /* Hiding in a keyboard-accesible way */
  opacity: 0;
  position: absolute;
  z-index: -1;
}
#vote > input:focus + label {
  outline: 1px dotted #999;          /* Keyboard friendly */
}
#vote > label {
  cursor: pointer;
  display: block;
  width: 0;
  border: 50px solid;               /* We will make them look like... */
  border-color: black transparent;  /* ...triangles using borders */
}
#upvote + label {                   
  border-top: none;                 /* Triangulating */
}
#downvote + label {                 
  border-bottom: none;              /* Triangulating */
  margin-top: 15px;                 /* Space between triangles */
}
#vote > input:checked + label {     
  border-color: orange transparent; /* Highlight chosen option */
}
#vote > #novote-label { 
  display: none;                    /* Hide it by default */
  position: absolute;               /* Take it out of the normal flow */
  border-top: none;
  border-color: transparent;
}
#upvote:checked ~ #novote-label {   /* Display it overlapping upvote */
  display: block;
  top: 0;
}
#downvote:checked ~ #novote-label { /* Display it overlapping downvote */
  display: block;
  bottom: 0;
}
<div id="vote">
  <input type="radio" name="vote" value="+1" id="upvote" />
  <label for="upvote"></label>
  <input type="radio" name="vote" value="-1" id="downvote" />
  <label for="downvote"></label>
  <input type="radio" name="vote" value="0" id="novote" />
  <label for="novote" id="novote-label"></label>
</div>

上面的代码片段使用边框来模拟三角形/箭头。同样,背景图片也可以被使用。

#vote {
    position: relative;
}
#vote > input {
    display: none;
}
#vote > label {
    cursor: pointer;
    display: block;
    width: 20px;
    height: 16px;
    background-image: url('http://i.stack.imgur.com/36F2b.png');
}
#vote > #up {
    background-position: left top;
}
#vote > #down {
    background-position: left bottom;
}
#upvote:checked ~ #up {
    background-position: top;
}
#downvote:checked ~ #down {
    background-position: bottom;
}
#vote > #no {
    display: none;
    position: absolute;
    background: none;
}
#upvote:checked ~ #no {
    display: block;
    top: 0;
}
#downvote:checked ~ #no {
    display: block;
    bottom: 0;
}
<div id="vote">
    <input type="radio" name="vote" value="+1" id="upvote" />
    <label for="upvote" id="up"></label>
    <input type="radio" name="vote" value="-1" id="downvote" />
    <label for="downvote" id="down"></label>
    <input type="radio" name="vote" value="0" id="novote" />
    <label for="novote" id="no"></label>
</div>


1
1+.. 很棒的解决方案;逻辑很好! - Josh Crozier
谢谢,这正是我想要的。我尝试使用你的解决方案来调整我的代码,但我做错了什么,你能帮忙检查一下吗? - SoKeT
是的,我终于意识到为什么我的代码不起作用了。与你不同的是,我单独指定了每个按钮的属性,所以当我添加了no-vote时它没有任何尺寸。设置宽度和高度解决了这个问题。非常感谢你的帮助。 - SoKeT
1
隐藏输入并设置标签样式会导致键盘无法访问。 - danielnixon
@danielnixon 很好的建议。我已经添加了一个可通过键盘访问的替代方案。 - Oriol

0

你有一个纯CSS的解决方案,这里提供了一个JavaScript版本。它在“投票”按钮的祖先上放置了一个点击监听器。当点击并选中一个按钮时,它会取消同一容器中其他投票按钮的选中状态,以便每次只有一个投票被选中。它适用于任意数量的投票按钮(例如+1、+2等)。

如果单击复选框取消选中状态(例如取消投票),则函数不执行任何操作。确保脚本放置在容器之后,或使用加载事件。监听器可以附加到任意数量的容器上,只要每个容器仅包含一个按钮集即可。

<!-- A container for the vote buttons -->
<div id="voteContainer">
  <input type="checkbox" name="vote" value="+1" id="upvote">
  <label for="upvote" id="up">Up</label>
  <br>
  <input type="checkbox" name="vote" value="-1" id="downvote">
  <label for="downvote" id="down">Down</label>
</div>

<script>
(function() {
  var div = document.getElementById('voteContainer');
  if (div) div.onclick = setVote;

  // Looks at the state of els
  function setVote(event) {
    event = event || window.event;
    var target = event.target || event.srcElement;


    // If target checkbox is checked, uncheck all others
    if (target.checked) {
      var els = this.querySelectorAll('input[name=' + target.name + ']')

      for (var i=0; i<els.length; i++) {
        els[i].checked = els[i] === target;
      }
    }
  }
}());
</script>

以上代码适用于所有现代浏览器和 IE8 及以上版本。如果需要在旧版浏览器中使用,也可以很容易地实现,尤其是如果页面中只有一个投票按钮集合(只需要对 var els = ... 进行少量修改即可)。

标签元素不需要为脚本设置ID即可正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接