kevinaskevin
kevinaskevin

Reputation: 299

Handing Metric to Inch conversion on a webpage in Javascript without cookies

I have a website that holds technical information. I want to convert Imperial units (inches) to Metric units (millimeters) based off the user's preference. My hosting provider does not allow use of php on webpages but I can use Javascript.

My original plan was to have a button that flips a switch to set a UnitPreference cookie that holds the customer preference for Metric/Imperial units. I have found using cookies in Javascript to be... quite unwieldy. Is there a way to accomplish my goal without cookies?

Upvotes: 1

Views: 1206

Answers (2)

Professor Abronsius
Professor Abronsius

Reputation: 33813

If you are using / coding in HTML5 you could use offline / session storage rather than cookies. The syntax is easier but it does rely upon the user allowing your site to store data.

Update [ 1.9.2018 ]

I noticed today that this answer received another upvote but perhaps might have been better as a simple comment. To support the above comment ( re storage ) I knocked up a little demo.

<!doctype html>
<html>
    <head>
        <meta charset='utf-8' />
        <title>Unit Preferences Storage with Javascript & localStorage</title>
        <style>
            body{
                display:flex;
                align-items:center;
                justify-content:center;
                height:100vh;
                width:100%;
                box-sizing:border-box;
                padding:0;
                margin:0;
            }
            input[type='button']{
                padding:1rem;
                color:green;
                margin:auto;
                align-self:center;
            }
        </style>
        <script>

            /* Event handler common flags */
            const flags={
                once:false,
                capture:true,
                passive:true
            };

            /* Unit preferences matrix */
            const matrix={
                1:{
                    name:'Metric',
                    unit:'mm',
                    conversion:25.4
                },
                0:{
                    name:'Imperial',
                    unit:'inches',
                    conversion:0.0393701
                }
            };

            /* Simple object to manipulate storage */
            const StoreFactory=function( name, type ){
                'use strict';
                const engine = type.toLowerCase() === 'local' ? localStorage : sessionStorage;

                const set=function( data ){
                    engine.setItem( name, JSON.stringify( data ) );
                };
                const get=function(){
                    return exists( name ) ? JSON.parse( engine.getItem( name ) ) : false;
                };
                const remove=function(){
                    engine.removeItem( name );
                };
                const exists=function(){
                    return engine.getItem( name )==null ? false : true;
                };
                return Object.freeze({
                    set,
                    get,
                    exists,
                    remove
                });
            }









            /* Run-time configuration */
            const storename='UnitPreference';
            const storetype='local'; // "session" is volatile ~ ie: exists only for current session

            /* Invoke the storeage object */
            let oStore=new StoreFactory( storename, storetype );



            /* Event handler that triggers storage of user choice */
            const switcher=function(e){
                'use strict';
                let payload = matrix[ e.target.dataset.state ];
                    payload.state = e.target.dataset.state;

                oStore.set( payload );

                e.target.dataset.state = 1 - e.target.dataset.state;
                e.target.value=payload.name;
            };


            /* Bind DOM elements and event handlers rather than inline event handlers... */
            const bindEvents=function(){
                'use strict';
                let json=false;

                let bttn=document.querySelector( 'input[type="button"][name="units"]' );
                    bttn.addEventListener( 'click', switcher.bind( bttn ), flags );
                    /*
                        If the user has set his/her choice for units then we need
                        re-establish initial conditions for button on page load
                        should they leave & return etc
                    */
                    if( json=oStore.get() ){
                        bttn.dataset.state = 1 - json.state;
                        bttn.value = json.name;

                        /* 
                            potentially you can now access the value stored in the localStorage object
                            for whatever conversion purposes you have...
                        */
                    }
                /* other event listeners ... */
            };

            document.addEventListener( 'DOMContentLoaded', bindEvents, flags );
        </script>
    <body>
        <!-- 
            A standard button with default settings. 
            The value will change when clicked and 
            then on subsequent page loads also.
        -->
        <input type='button' name='units' value='Choose Units ' data-state=1 />
    </body>
</html>

Upvotes: 2

RobG
RobG

Reputation: 147413

Here's a simple demo of changing a style rule. There are much more sophisticated ways of going about it, but this is a simple method for this application. By default you could show either one or both units.

The rules to be changed must exist in an existing style sheet.

<style type="text/css">
.metric {
  display: inline;
}
.imperial {
  display: none;
}
</style>

<script>

// Search style sheets for selector. If found, set
// prop to value. Stops when first selector found
function setRuleValue(selector, prop, value) {
  selector = selector.toLowerCase();

  // Get the style sheets
  var sheet, sheets = document.styleSheets;
  var j, rule, rules;

  if (sheets) {

    // Search style sheets for rule
    for (var i=0, iLen=sheets.length; i<iLen; i++) {
      sheet = sheets[i];
      j = 0;

      // Choose .cssRules or .rules, whichever is supported
      while (rule = (sheet.cssRules[j] || sheet.rules[j])) {
        j++;

        if (rule.selectorText.toLowerCase() == selector) {
          rule.style[prop] = value;

          // If had success, return true
          return true;
        }
      }
    }
  }
  // If get here, didn't find rule so return false
  return false;
}

// Turn metric on, imperial off
function showMetric() {
  setRuleValue('.metric',   'display', '');
  setRuleValue('.imperial', 'display', 'none' );
}

// Turn imperial on, metric off
function showImperial() {
  setRuleValue('.metric',   'display', 'none');
  setRuleValue('.imperial', 'display', '' );
}

</script>

<div>This part is <span class="imperial">3&ensp;inches</span><span class="metric">25.4&ensp;mm</span> long</div>
<br>
<button onclick="showMetric()">Show metric</button>
<button onclick="showImperial()">Show imperial</button>

If you have a table, you can create a colgroup for each of metric and imperial, then just change the which colgroup is hidden or shown. By default, show both.

Upvotes: 1

Related Questions