Home > database >  How does this star rating work, how does it know how to select all the stars below the one you click
How does this star rating work, how does it know how to select all the stars below the one you click

Time:03-05

This is pure HTML and CSS for selecting the stars.

I do get what the js code does but I fail to understand how the stars are selected when I click on a star and al the stars below that one get selected

.rating {
  float: left;
}


/* :not(:checked) is a filter, so that browsers that don’t support :checked don’t 
       follow these rules. Every browser that supports :checked also supports :not(), so
       it doesn’t make the test unnecessarily selective */

.rating:not(:checked)>input {
  position: absolute;
  top: -9999px;
  clip: rect(0, 0, 0, 0);
}

.rating:not(:checked)>label {
  float: right;
  width: 1em;
  padding: 0 .1em;
  overflow: hidden;
  white-space: nowrap;
  cursor: pointer;
  font-size: 200%;
  line-height: 1.2;
  color: #ddd;
  text-shadow: 1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating:not(:checked)>label:before {
  content: '★ ';
}

.rating>input:checked~label {
  color: #f70;
  text-shadow: 1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating:not(:checked)>label:hover,
.rating:not(:checked)>label:hover~label {
  color: gold;
  text-shadow: 1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating>input:checked label:hover,
.rating>input:checked label:hover~label,
.rating>input:checked~label:hover,
.rating>input:checked~label:hover~label,
.rating>label:hover~input:checked~label {
  color: #ea0;
  text-shadow: 1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0, 0, 0, .5);
}

.rating>label:active {
  position: relative;
  top: 2px;
  left: 2px;
}


/* end of Lea's code */


/*
     * Clearfix from html5 boilerplate
     */

.clearfix:before,
.clearfix:after {
  content: " ";
  /* 1 */
  display: table;
  /* 2 */
}

.clearfix:after {
  clear: both;
}


/*
     * For IE 6/7 only
     * Include this rule to trigger hasLayout and contain floats.
     */

.clearfix {
  *zoom: 1;
}


/* my stuff */

#status,
button {
  margin: 20px 0;
}
<div id="status"></div>
<form id="ratingForm">
  <fieldset >
    <legend>Please rate:</legend>
    <input type="radio" id="star5" name="rating" value="5" /><label for="star5" title="Rocks!">5 stars</label>
    <input type="radio" id="star4" name="rating" value="4" /><label for="star4" title="Pretty good">4 stars</label>
    <input type="radio" id="star3" name="rating" value="3" /><label for="star3" title="Meh">3 stars</label>
    <input type="radio" id="star2" name="rating" value="2" /><label for="star2" title="Kinda bad">2 stars</label>
    <input type="radio" id="star1" name="rating" value="1" /><label for="star1" title="Sucks big time">1 star</label>
  </fieldset>
  <div ></div>
  <button >Submit</button>
</form>

enter image description here

CodePudding user response:

There is a few things going on. I have simplified it with more modern CSS.

  1. The items in the fieldset are reversed. This is important because CSS has a selector for elements that come after a certain selector but not before. The tilde, ~, is used to select every sibling that comes after an element.
  2. The checked radio button :checked is used to determine which is the target element, after which all elements should get a certain style applied.

These things combined allow colouring everything after the checked input. Since the list is reversed, it appears to the user as if everything before checked input was coloured. Additionally, the checked input itself is also coloured.

.rating>input:checked::before,
.rating>input:checked~input::before 

In this example, the pseudo-element before is used to get the star via content: '⭐'. The colouring is done by removing the colour in the default state through a CSS filter: filter: grayscale(100%) contrast(200%); and unsetting the filter when the checked, and sibling after checked, condition applies.

.rating {
  display: flex;
  flex-direction: row-reverse;
  width: 320px;
  padding-bottom: 1rem;
}

.rating>input {
  flex: 1 1 0%;
  display: grid;
  place-content: center;
}

.rating>input::before {
  content: '⭐';
  filter: grayscale(100%) contrast(200%);
  font-size: 200%;
}

.rating>input:checked::before,
.rating>input:checked~input::before {
  filter: unset;
}
<fieldset >
  <legend>Please rate:</legend>
  <input type="radio" id="star5" name="rating" value="5" />
  <input type="radio" id="star4" name="rating" value="4" />
  <input type="radio" id="star3" name="rating" value="3" checked />
  <input type="radio" id="star2" name="rating" value="2" />
  <input type="radio" id="star1" name="rating" value="1" />
</fieldset>

Using these techniques, you could easily expand this with the hover state, as in your example.

Another thing that was used in your example is the which is similar to ~ but it only applies to the first silibing instead of all sibling. It appears to be redundant.

  • Related