skip to Main Content

Currently the checkboxes do as such.

Ruby Red alters the first svg
Forest Green alters the second svg
Ocean Blue alters the third svg

What I would like to do is make it dependent on what is selected first. So if Ocean Blue is selected first it would change the first svg. If the second was Ruby Red it would change the second svg.

If there is also a way to do this where the svgs have unique class names instead of all being "svg-icon" that would really help me with structure.

I guess one final thing I am looking to achieve is if any of the checkboxes are deselected it "free’s up that spot in essence" So if the first svg was freed up because of deselection the next checkbox selection would take its place.

Any help or pointers in the right direction would be really appreciated. I am trying to make a visualizer to sell my products like soap that I do as a hobby.

const checkboxes = document.querySelectorAll('.checkbox');
const svgs = document.querySelectorAll('.svg-icon');
let selectedIndices = [];

checkboxes.forEach((checkbox, index) => {
  checkbox.addEventListener('click', function() {
    const className = this.value.replace(/[_d]/g, '');
    const targetIndex = selectedIndices.indexOf(index);
    if (this.checked) {
      if (targetIndex === -1) {
        selectedIndices.push(index);
        svgs[index].querySelector('path').setAttribute('class', className);
      }
    } else {
      if (targetIndex !== -1) {
        selectedIndices.splice(targetIndex, 1);
        svgs[index].querySelector('path').removeAttribute('class');
      }
    }
  });
});
.svg-icon {
  width: 100px;
  height: 100px;
}

.rubyRed {
  fill: #9b111e;
}

.forestGreen {
  fill: #228b22;
}

.oceanBlue {
  fill: #00E7FF;
}
<input type="checkbox" class="checkbox" value="rubyRed_1">
<label>Ruby Red</label>

<input type="checkbox" class="checkbox" value="forestGreen_2">
<label>Forest Green</label>

<input type="checkbox" class="checkbox" value="oceanBlue_3">
<label>Ocean Blue</label>

<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3"/>
  </svg>

<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3"/>
  </svg>

<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3"/>
  </svg>

Im mostly unsure of where to start or what would be the best type of logic to deal with these interactions.

2

Answers


  1. Here is my full solutions, there are some comments which explain what I did.

    <!DOCTYPE html>
    <html>
      <head>
        <style>
          .svg-icon {
            width: 100px;
            height: 100px;
          }
    
          .rubyRed {
            fill: #9b111e;
          }
    
          .forestGreen {
            fill: #228b22;
          }
    
          .oceanBlue {
            fill: #00E7FF;
          }
        </style>
      </head>
      <body>
        <input type="checkbox" class="checkbox" value="rubyRed_1">
        <label>Ruby Red</label>
        <input type="checkbox" class="checkbox" value="forestGreen_2">
        <label>Forest Green</label>
        <input type="checkbox" class="checkbox" value="oceanBlue_3">
        <label>Ocean Blue</label>
        <div id="svg-wrapper">
          <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
          </svg>
          <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
          </svg>
          <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
          </svg>
        </div>
        <script>
          const checkboxes = document.querySelectorAll('.checkbox');
          const svgWrapper = document.querySelector('#svg-wrapper');
          const amountOfSvgs = 3
          const selectedIndices = new Map(); // we can use a map to store the position and state
          const availableIndices = [] // store which svgs are free
          for (let i = amountOfSvgs - 1; i >= 0; i--) { // setup our array [2, 1, 0]
            availableIndices.push(i)
          }
          checkboxes.forEach((checkbox, index) => {
            checkbox.addEventListener('click', function() {
              const className = this.value.replace(/[_d]/g, '');
              const svgPosition = selectedIndices.get(index) // we are using index as an identifier
              if (this.checked) {
                if (!Number.isInteger(svgPosition)) { // isInteger here because 0 evaluates to false
                  const position = availableIndices.pop();
                  console.log(`index: ${index} pos: ${position}`)
                  selectedIndices.set(index, position) // we store the position of which svg we changed
                  svgWrapper.children[position].querySelector('path').setAttribute('class', className);
                }
              } else {
                if (Number.isInteger(svgPosition)) {
                  svgWrapper.children[svgPosition].querySelector('path').removeAttribute('class');
                  availableIndices.push(svgPosition)
                  selectedIndices.delete(index)
                  // sort the list so we take up starting from index 0
                  availableIndices.sort((a, z) => z - a)
                }
              }
            });
          });
        </script>
      </body>
    </html>
    

    If there is also a way to do this where the svgs have unique class
    names instead of all being "svg-icon" that would really help me with
    structure.

    What I did was create a div wrapper and put all the svg in it then call it with children.

    const svgWrapper = document.querySelector('#svg-wrapper');
    // code here
    svgWrapper.children[position].querySelector('path').setAttribute('class', className);
    

    What I would like to do is make it dependent on what is selected
    first. So if Ocean Blue is selected first it would change the first
    svg. If the second was Ruby Red it would change the second svg.

    What I used here was a Map to store which SVG was being shown and at which SVG position

    const selectedIndices = new Map(); // we can use a map to store the position and state
    

    I guess one final thing I am looking to achieve is if any of the
    checkboxes are deselected it "free’s up that spot in essence" So if
    the first svg was freed up because of deselection the next checkbox
    selection would take its place.

    For this what I did was to have an array which stored which SVGs were free. Then we can keep track of where to add back the svg on the next click.

    const availableIndices = [] // store which svgs are free
    for (let i = amountOfSvgs - 1; i >= 0; i--) { // setup our array [2, 1, 0]
        availableIndices.push(i)
    }
    

    I prefilled the positions which were available so we can just grab the first available position (0).

    Login or Signup to reply.
  2. Here is my solution.

    Push or Remove the class name from array result.

    Reset svgs’ class by result every time checkboxes was clicked.

    const checkboxes = document.querySelectorAll(".checkbox");
    const svgs = document.querySelectorAll(".svg-icon");
    const result = [];
    const selected = ({ target }) => {
      const className = target.value.replace(/[_d]/g, "");
      if (target.checked) {
        result.push(className);
      } else {
        result.splice(result.indexOf(className), 1);
      }
      svgs.forEach((elem, i) => {
        const path = elem.querySelector("path");
        path.classList.remove(...path.classList);
        if (result[i]) path.classList.add(result[i]);
      });
    };
    
    checkboxes.forEach((checkbox) => {
      checkbox.checked = false;
      checkbox.addEventListener("click", selected);
    });
    .svg-icon {
      width: 100px;
      height: 100px;
    }
    
    .rubyRed {
      fill: #9b111e;
    }
    
    .forestGreen {
      fill: #228b22;
    }
    
    .oceanBlue {
      fill: #00E7FF;
    }
    <label><input type="checkbox" class="checkbox" value="rubyRed_1">Ruby Red</label>
    <label><input type="checkbox" class="checkbox" value="forestGreen_2">Forest Green</label>
    <label><input type="checkbox" class="checkbox" value="oceanBlue_3">Ocean Blue</label>
    <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
        </svg>
    <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
        </svg>
    <svg class="svg-icon" viewBox="0 0 20 20">
            <path fill="#000000" d="M10,0C4.5,0,0,4.5,0,10c0,5.5,4.5,10,10,10s10-4.5,10-10C20,4.5,15.5,0,10,0z M15.3" />
        </svg>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search