CSS 选择所有以前的兄弟姐妹以获得星级

Jac*_*kal 19 javascript css

我想做一个星级控制,但我似乎无法找到一种方法来选择悬停时所有以前的兄弟姐妹。这种东西是否存在或者我必须使用javascript?

span {
  display:inline-block;
  width: 32px;
  height: 32px;
  background-color:#eee;
}

span:hover {
  background-color:red;
}
Run Code Online (Sandbox Code Playgroud)
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
Run Code Online (Sandbox Code Playgroud)

Dav*_*mas 11

我似乎无法找到一种在悬停时选择所有先前兄弟姐妹的方法

不幸的是,CSS 只能定位并因此选择 DOM 中的后续元素。值得庆幸的是,这当然可以用 CSS 模拟或用 JavaScript 启用。

首先,CSS 方法需要 CSS Grid 或 CSS Flexbox 来调整页面上元素的顺序:

*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

div {
  /* to place a 1em gap between items, applicable
     to both Grid and Flexbox: */
  gap: 1em;
  width: 80vw;
  margin: 1em auto;
}

div.withFlex {
  /* Using flexbox layout: */
  display: flex;
  /* In the HTML you might have noticed that the '5 star'
     element comes before the '1 star'...'4 star' element,
     this property reverses the order of the flex-items
     (the <span> elements) in the flex-box layout: */
  flex-direction: row-reverse;
  /* spacing the elements apart, this approach places the
     available space (after the element-sizes have been
     calculated) between the elements: */
  justify-content: space-between;
}

div.withFlex span {
  border: 1px solid #000;
  flex: 1 1 auto;
}


/* here we use Grid layout: */

div.withGrid {
  display: grid;
  /* we force the grid-items (the <span> elements) to
     flow into columns rather than rows: */
  grid-auto-flow: column;
  /* here we cause the layout - again - to be reversed,
     flowing from right-to-left: */
  direction: rtl;
}

div.withGrid span {
  border: 1px solid currentcolor;
  text-align: left;
}


/* here we select the <span> that the user hovers over,
   plus any subsequent siblings, and style them differently;
   as the subsequent elements appear - visually - before the
   hovered-<span> this gives the illusion that we're selecting
   previous elements in the DOM: */

span:hover,
span:hover~span {
  color: #f90;
  border-color: currentcolor;
}
Run Code Online (Sandbox Code Playgroud)
<div class="withFlex">
  <span>5 stars</span>
  <span>4 stars</span>
  <span>3 stars</span>
  <span>2 stars</span>
  <span>1 star</span>
</div>

<div class="withGrid">
  <span>5 stars</span>
  <span>4 stars</span>
  <span>3 stars</span>
  <span>2 stars</span>
  <span>1 star</span>
</div>
Run Code Online (Sandbox Code Playgroud)

除了上述之外,假设您希望元素能够保持被选中状态——同时仍然使用 CSS 和 HTML——那么使用 some <input>and<label>元素也是可能的:

*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

div {
  /* to place a 1em gap between items, applicable
     to both Grid and Flexbox: */
  gap: 1em;
  width: 80vw;
  margin: 1em auto;
}

input[type=radio] {
  position: absolute;
  top: -10000px;
  left: -10000px
}

label {
  border: 1px solid currentcolor;
  cursor: pointer;
}

div.withFlex {
  /* Using flexbox layout: */
  display: flex;
  /* In the HTML you might have noticed that the '5 star'
     element comes before the '1 star'...'4 star' element,
     this property reverses the order of the flex-items
     (the <span> elements) in the flex-box layout: */
  flex-direction: row-reverse;
  /* spacing the elements apart, this approach places the
     available space (after the element-sizes have been
     calculated) between the elements: */
  justify-content: space-between;
}

div.withFlex label {
  flex: 1 1 auto;
}


/* here we use Grid layout: */

div.withGrid {
  display: grid;
  /* we force the grid-items (the <span> elements) to
     flow into columns rather than rows: */
  grid-auto-flow: column;
  /* here we cause the layout - again - to be reversed,
     flowing from right-to-left: */
  direction: rtl;
}

div.withGrid label {
  direction: ltr;
}


/* here we select the <span> that the user hovers over,
   plus any subsequent siblings, and style them differently;
   as the subsequent elements appear - visually - before the
   hovered-<span> this gives the illusion that we're selecting
   previous elements in the DOM: */
label:hover,
label:hover~label {
  color: #f90f;
  border-color: currentcolor;
}

/* here we select all <label> elements that follow an <input>
   of type=radio (using an attribute-selector) which is checked: */
input[type=radio]:checked~label {
  color: #f90c;
  border-color: currentcolor;
}
Run Code Online (Sandbox Code Playgroud)
<div class="withFlex">

  <!-- because we're styling the <label> elements based
       on the state (checked/unchecked) of the <input>
       elements we have to place the relevant <input>
       before the affected <label> in the DOM; which is
       why they precede the element that's being styled.
       While the :focus-within pseudo-class exists there
       is (as yet) no comparable ':checked-within', and
       the :has() pseudo-class does not yet (in 2020)
       exist; JavaScript could be used but this demo is
       to show HTML/CSS methods rather than JS: -->
  <input id="withFlexInput5" type="radio" name="rating1" />
  <label for="withFlexInput5">
    5 stars
  </label>

  <input id="withFlexInput4" type="radio" name="rating1" />
  <label for="withFlexInput4">
    4 stars
  </label>

  <input id="withFlexInput3" type="radio" name="rating1" />
  <label for="withFlexInput3">
    3 stars
  </label>

  <input id="withFlexInput2" type="radio" name="rating1" />
  <label for="withFlexInput2">
    2 stars
  </label>

  <input id="withFlexInput1" type="radio" name="rating1" />
  <label for="withFlexInput1">
    1 star
  </label>
</div>

<div class="withGrid">

  <input id="withGridInput5" type="radio" name="rating2" />
  <label for="withGridInput5">
    5 stars
  </label>

  <input id="withGridInput4" type="radio" name="rating2" />
  <label for="withGridInput4">
    4 stars
  </label>

  <input id="withGridInput3" type="radio" name="rating2" />
  <label for="withGridInput3">
    3 stars
  </label>

  <input id="withGridInput2" type="radio" name="rating2" />
  <label for="withGridInput2">
    2 stars
  </label>

  <input id="withGridInput1" type="radio" name="rating2" />
  <label for="withGridInput1">
    1 stars
  </label>
</div>
Run Code Online (Sandbox Code Playgroud)


Mes*_*Qin 5

// obtain all spans from DOM
const spans = document.querySelectorAll('span');
// set a variable at global scope as indicator
let flag = false; 

// add event listener to each span
spans.forEach((sp, j)=>{
    sp.addEventListener('click', ()=>{
    // if clicked, then not dismissing the background colour after mouse leave
    flag = true;
    // reassign all spans back to original grey
    spans.forEach(dsp=>{
        dsp.style.backgroundColor = '#eee';
    });
    // assign bg to red of the spans from 0 to clicked index
    Array.from(new Array(j+1), (x, i) => i).forEach(ind=>{
        spans[ind].style.backgroundColor = 'red';
    });
  });
    // redo if mouse enters
    sp.addEventListener('mouseenter', ()=>{
    flag = false;
  });
    // if any span is hovered
    sp.addEventListener('mouseover', ()=>{
    // reassign all spans back to original grey
    spans.forEach(dsp=>{
        dsp.style.backgroundColor = '#eee';
    });
    // assign bg to red of the spans from 0 to clicked index
    Array.from(new Array(j+1), (x, i) => i).forEach(ind=>{
        spans[ind].style.backgroundColor = 'red';
    });
  });
  // in moseleave, only save the background colour if click happened
  sp.addEventListener('mouseleave', ()=>{
    if(!flag){
      spans.forEach(dsp=>{
        dsp.style.backgroundColor = '#eee';
      });
    }
  });
});
Run Code Online (Sandbox Code Playgroud)
span {
  display:inline-block;
  width: 32px;
  height: 32px;
  background-color:#eee;
}

span:hover {
  background-color:red;
  opacity: 0.8;
  cursor: pointer;
}
Run Code Online (Sandbox Code Playgroud)
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
Run Code Online (Sandbox Code Playgroud)

  • 可以通过添加解释来改进这个答案。 (5认同)

Rou*_*ica 5

这里还有其他使用FlexboxCSS Grid 的CSS 解决方案。

但是,如果你很高兴去老派,同样的效果可以实现:

float: right;
Run Code Online (Sandbox Code Playgroud)

工作示例:

float: right;
Run Code Online (Sandbox Code Playgroud)
div {
float: left;
width: 180px;
}

span {
float: right;
display: inline-block;
width: 32px;
height: 32px;
margin-left: 4px;
background-color: #eee;
cursor: pointer;
}

span:hover,
span:hover ~ span {
background-color: red;
}
Run Code Online (Sandbox Code Playgroud)