Search code examples
javascripthtmlcss

Custom Radio Button Accuracy


I currently have two radio buttons set up with custom styles. The styling and radio button code was found mainly online, with a few things that I changed to fit my need. The issue I am having is that if you click outside of the box, it still selects up to a certain distance, but does not trigger the associated click event.

let calcType = document.querySelector('#calcType')
let calcTypeLabels = document.querySelectorAll('.styled-label span')

calcTypeLabels.forEach((check) => {
check.addEventListener('click', (ev) => {
    let parent = ev.target.parentElement
    if (parent.querySelector('input').checked == false) {
        parent.querySelector('input').checked = true
        console.log("clicked radio button")
        // setCalcType(parent.querySelector('input').value)
        // currentType = parent.querySelector('input').value
        // setRangeStyle()
    }
})
})
/* Custom Radio Buttons */

.styled-radio {
  display: none;
}

.styled-label {
  display: inline-block;
  padding: 5px 10px;
  cursor: pointer;
  /* This is the element that senses the clicks */
}

.styled-label span {
  position: relative;
  line-height: 22px;
}

.styled-label span:before,
.styled-label span:after {
  content: "";
}

.styled-label span:before {
  border: 1px solid #222021;
  width: 20px;
  height: 20px;
  /* margin-right: 10px; */
  display: inline-block;
  vertical-align: top;
}

.styled-label span:after {
  width: 14px;
  height: 14px;
  position: absolute;
  top: 2px;
  left: 4px;
  transition: 300ms;
  opacity: 0;
}

.styled-label input:checked + span:after {
  opacity: 1;
}

.radio-label::after {
  pointer-events: none;
}

.styled-label span:after {
    background-color: red;
}
<!DOCTYPE html>
<html>
    <head>
        <link href="./example.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <label class="styled-label">
            <label class="radio-label" for="calcType">Difficulty</label>
            <br />
            <input
                class="styled-radio"
                id="calcType"
                type="radio"
                name="calcType"
                value="diff"
                checked
            />
            <span></span>
        </label>

        <label class="styled-label">
            <label class="radio-label" for="calcType">Study Days</label>
            <br />
            <input
                class="styled-radio"
                id="calcType"
                type="radio"
                name="calcType"
                value="days"
            />
            <span></span>
        </label>

        <script src="./example.js"></script>
    </body>
</html>

Is there a way I could fix this by either stopping click events on certain elements (tried a few ::after things, but nothing was working), or possibly using a different way of styling the radio buttons.


Solution

  • The HTML code is wrong; the id needs to be unique and other things must be changed. When you put the for attribute on the label, you are telling that if I click on the label, I will select the radio input, that is why it's selected when you click next to the input. No, for the problem you are facing if you want to trigger an event every time you select the radio button, you can use the change event instead of the click and put the event on the input itself. Like this:

    
    let calcTypeLabels = document.querySelectorAll('[name="calcType"]')
    
    
    
        calcTypeLabels.forEach((check) => {
          check.addEventListener('change', (ev) => {
            console.log("clicked radio button")
    
            let parent = ev.target.parentElement
            if (parent.querySelector('input').checked == false) {
              parent.querySelector('input').checked = true
              console.log("clicked radio button")
            }
          })
        })
    

    Now every time the value change you can listen to it.

    But the main problem you have in your JS is that you are attaching the event on the span inside the label which has the width and the height of the input, but when you click the label or the surrounding space the for will trigger a change event and change the value automatically. But the click will not fire because you did not click on the span element. Notice the selector in the function querySelectorAll.

    If you want to register for the click event specifically, tell me, and I can check it.