notoriousb1t

Messin' with SVG

Incorporating SVG into websites is one of the easiest ways to inject a fun and interactive experience into your website. To show this off, I built a simple rating control using Illustrator and a little bit of JavaScript.

Finding a suitable icon

First things first, I had to find a decent icon. I thought about using rectangles or stars, but that is flat-out boring. I tried a few other things, but wasn't satisfied.

A friend suggested a crown because I have the same name as Biggie, so I found a good one on iconmonstr. I highly recommend iconmonstr. Their site is dead simple to use and is great when you need assets for quick wireframes.

Manipulating images in Illustrator

Iconmonstr provides SVG's for each of their icons, so all that is left to do from a design perspective is do some very basic image manipulation in Illustrator. I opened the SVG, changed the artboard to have room for about five of them, and then copied the icon four times across.

To get the SVG code, I saved the file as an SVG 1.1 and opened the file in a text editor and removed additional adobe markup.

Putting SVG in the HTML

I copied the SVG code into my HTML and started decorating it with classes. I added two classes to the SVG to properly style it; the .rating-icons class will handle how the rating control interacts with HTML and the .rating-icon to colorize each icon and set borders. Here is the SVG code:

Icon code

    <svg class="rating-icons" viewBox="0 0 2564 512">
        <path class="rating-icon" d="M427.7,376.2v51.5H84.3v-51.5H427.7z M427.7,118.7c-25.8,0-45.7,28.9-27.4,54.8c9.2,13,7,20.9,0.8,30
        c-13.1,19.4-32.1,35.3-59.2,35.3c-43.3,0-66.2-35.8-70.9-66.1c-1.6-10.5-2.6-18.4,7.5-28.2c6.9-6.7,11.9-15.5,11.9-25.9
        c0-19-15.4-34.3-34.3-34.3c-18.9,0-34.3,15.4-34.3,34.3c0,10.4,5,19.2,11.9,25.9c10.1,9.8,9.1,17.7,7.5,28.2
        c-4.8,30.3-27.6,66.1-70.9,66.1c-27,0-46-16-59.2-35.3c-6.2-9.1-8.4-17,0.8-30c18.3-25.9-1.7-54.8-27.4-54.8
        C65.4,118.7,50,134,50,153c0,13.7,8,25.7,19.6,31c14.2,6.5,14.7,22.6,14.7,37.3v120.5h343.3V221.3c0-14.7,0.5-30.8,14.7-37.3
        c11.7-5.4,19.6-17.3,19.6-31C462,134,446.6,118.7,427.7,118.7z" />
        <path class="rating-icon" d="M945.7,376.2v51.5H602.3v-51.5H945.7z M945.7,118.7c-25.8,0-45.7,28.9-27.4,54.8c9.2,13,7,20.9,0.8,30
        c-13.1,19.4-32.1,35.3-59.2,35.3c-43.3,0-66.2-35.8-70.9-66.1c-1.6-10.5-2.6-18.4,7.5-28.2c6.9-6.7,11.9-15.5,11.9-25.9
        c0-19-15.4-34.3-34.3-34.3c-18.9,0-34.3,15.4-34.3,34.3c0,10.4,5,19.2,11.9,25.9c10.1,9.8,9.1,17.7,7.5,28.2
        c-4.8,30.3-27.6,66.1-70.9,66.1c-27,0-46-16-59.2-35.3c-6.2-9.1-8.4-17,0.8-30c18.3-25.9-1.7-54.8-27.4-54.8
        c-18.9,0-34.3,15.4-34.3,34.3c0,13.7,8,25.7,19.6,31c14.2,6.5,14.7,22.6,14.7,37.3v120.5h343.3V221.3c0-14.7,0.5-30.8,14.7-37.3
        c11.7-5.4,19.6-17.3,19.6-31C980,134,964.6,118.7,945.7,118.7z" />
        <path class="rating-icon" d="M1453.7,376.2v51.5h-343.3v-51.5H1453.7z M1453.7,118.7c-25.8,0-45.7,28.9-27.4,54.8c9.2,13,7,20.9,0.8,30
        c-13.1,19.4-32.1,35.3-59.2,35.3c-43.3,0-66.2-35.8-70.9-66.1c-1.6-10.5-2.6-18.4,7.5-28.2c6.9-6.7,11.9-15.5,11.9-25.9
        c0-19-15.4-34.3-34.3-34.3c-18.9,0-34.3,15.4-34.3,34.3c0,10.4,5,19.2,11.9,25.9c10.1,9.8,9.1,17.7,7.5,28.2
        c-4.8,30.3-27.6,66.1-70.9,66.1c-27,0-46-16-59.2-35.3c-6.2-9.1-8.4-17,0.8-30c18.3-25.9-1.7-54.8-27.4-54.8
        c-18.9,0-34.3,15.4-34.3,34.3c0,13.7,8,25.7,19.6,31c14.2,6.5,14.7,22.6,14.7,37.3v120.5h343.3V221.3c0-14.7,0.5-30.8,14.7-37.3
        c11.7-5.4,19.6-17.3,19.6-31C1488,134,1472.6,118.7,1453.7,118.7z" />
        <path class="rating-icon" d="M1961.7,376.2v51.5h-343.3v-51.5H1961.7z M1961.7,118.7c-25.8,0-45.7,28.9-27.4,54.8c9.2,13,7,20.9,0.8,30
        c-13.1,19.4-32.1,35.3-59.2,35.3c-43.3,0-66.2-35.8-70.9-66.1c-1.6-10.5-2.6-18.4,7.5-28.2c6.9-6.7,11.9-15.5,11.9-25.9
        c0-19-15.4-34.3-34.3-34.3c-18.9,0-34.3,15.4-34.3,34.3c0,10.4,5,19.2,11.9,25.9c10.1,9.8,9.1,17.7,7.5,28.2
        c-4.8,30.3-27.6,66.1-70.9,66.1c-27,0-46-16-59.2-35.3c-6.2-9.1-8.4-17,0.8-30c18.3-25.9-1.7-54.8-27.4-54.8
        c-18.9,0-34.3,15.4-34.3,34.3c0,13.7,8,25.7,19.6,31c14.2,6.5,14.7,22.6,14.7,37.3v120.5h343.3V221.3c0-14.7,0.5-30.8,14.7-37.3
        c11.7-5.4,19.6-17.3,19.6-31C1996,134,1980.6,118.7,1961.7,118.7z" />
        <path class="rating-icon" d="M2449.7,376.2v51.5h-343.3v-51.5H2449.7z M2449.7,118.7c-25.8,0-45.7,28.9-27.4,54.8c9.2,13,7,20.9,0.8,30
        c-13.1,19.4-32.1,35.3-59.2,35.3c-43.3,0-66.2-35.8-70.9-66.1c-1.6-10.5-2.6-18.4,7.5-28.2c6.9-6.7,11.9-15.5,11.9-25.9
        c0-19-15.4-34.3-34.3-34.3c-18.9,0-34.3,15.4-34.3,34.3c0,10.4,5,19.2,11.9,25.9c10.1,9.8,9.1,17.7,7.5,28.2
        c-4.8,30.3-27.6,66.1-70.9,66.1c-27,0-46-16-59.2-35.3c-6.2-9.1-8.4-17,0.8-30c18.3-25.9-1.7-54.8-27.4-54.8
        c-18.9,0-34.3,15.4-34.3,34.3c0,13.7,8,25.7,19.6,31c14.2,6.5,14.7,22.6,14.7,37.3v120.5h343.3V221.3c0-14.7,0.5-30.8,14.7-37.3
        c11.7-5.4,19.6-17.3,19.6-31C2484,134,2468.6,118.7,2449.7,118.7z" />
      </svg>

I needed to create a target for initialization, so I wrapped the SVG with a div target with a class of .rating-control In order to let this interact with HTML forms, I also added a hidden input with a class of .rating-value. In this example, I am setting a default value of 4 for the rating control.

    <div class="rating-control">
      <input type="hidden" value="4" class="rating-value" />
      <svg class="rating-icons" viewBox="0 0 2564 512">
        ...
      </svg>
    </div>

Now it's time to make it a little prettier.

Making SVG stylish

Styling SVG has a different set of rules. Instead of setting an element's background, you have to set it's fill. Instead of settings a color, you have to set a stroke. here is how I dolled up the control:

    .rating-icons {
        width: 100%;
    }
    .rating-icon {
        width: 100%;
        height: 100%;
        cursor: pointer;
    }
    .rating-icon:not(.active) {
        fill: #FFFCE8;
        stroke: black;
        stroke-width: 2px;
    }
    .rating-icon.active {
        fill: #F7E66D;
        stroke: #333;
        stroke-width: 2px;
    }

I set the icons to be a dim yellow and a darker outline when they don't also have the active class, and a deeper yellow and lighter border when they have the active class. I also set the rating-icons class to get a width of 100%. This rule combined with a max-width can make this control responsive.

Being real with JavaScript

The last thing I needed to do is actually make the thing work! I chose to start with a vanilla JavaScript approach first but also included the same code utilizing jQuery as well.

Vanilla JavaScript code

    /**
    * define foreach for iterating over html elements
    */
    function forEach(list, fn) {
      var length = list.length;
      for (var x = 0; x < length; x++) {
        fn(x, list[x]);
      }
    }

    // find all of the elements in the rating control
    var control = document.querySelector('.rating-control');
    var input = control.querySelector('.rating-value');
    var icons = control.querySelectorAll('.rating-icon');

    /**
    * Update UI with new icon value
    */
    function updateUI() {
      // get new value.. assume 0 if no value in hidden input
      var val = input.value || 0;

      // set each rating icon to active or not
      forEach(icons, function(y, icon) {
        // toggle class active based on if y is less than the current value
        icon.classList.toggle('active', y < val);
      });
    }

    // add click handler to each rating icon to set the correct rating value
    forEach(icons, function(y, icon) {
      icon.addEventListener('click', function() {
        // 0 = 1, 1 = 2, etc.
        input.value = y+1;
        updateUI();
      });
    });

    // set initial values
    updateUI();

jQuery code

    // find all of the elements in the rating control
    var $control = $('.rating-control');
    var $input = $control.find('.rating-value');
    var $icons = $control.find('.rating-icon');

    /**
    * Update UI with new icon value
    */
    function updateUI() {
      // get new value.. assume 0 if no value in hidden input
      var val = $input.val() || 0;

      // set each rating icon to active or not
      $icons.each(function(y, icon) {
        // toggle class active based on if y is less than the current value
        icon.classList.toggle('active', y < val);
      });
    }

    // add click handler to each rating icon to set the correct rating value
    $icons.each(function(y, icon) {
      $(icon).click(function() {
        // 0 = 1, 1 = 2, etc.
        $input.val(y+1);
        updateUI();
      });
    });

    // set initial values
    updateUI();

Here is the general logic for the control:

  1. Find the control by the class name
  2. Find the children elements (input holder and icons)
  3. Define a way to update the UI. I toggle .active on and off
  4. Setup event listeners to update the input and force the UI to update when the icons are clicked
  5. Update the UI the first time

Here is the full example (jQuery version):

See the Pen Rating Control with SVG, jQuery by Christopher Wallis (@notoriousb1t) on CodePen.