1

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?

kevinaskevin
  • 299
  • 3
  • 20
  • localstorage or sessionstorage could be of help. – Cerbrus May 27 '14 at 11:25
  • Why not just publish both, wrap measurements in a span with class "metric" or "imperial" as appropriate. Then simply modify the related CSS rules to show the chosen units and hide the other. Minimal script and all values are converted at the server, once. The cookie just needs to remember which option was selected. – RobG May 27 '14 at 11:30
  • @RobG I like that idea. Can you give an example of what the CSS would look like? – kevinaskevin May 27 '14 at 11:39
  • Assuming: `15 cm – Cerbrus May 27 '14 at 12:04
  • 1
    @fortmac—there are lots of questions about that, e.g. [modify a css rule object with javascript](http://stackoverflow.com/questions/13528512/modify-a-css-rule-object-with-javascript). – RobG May 27 '14 at 12:18
  • In Windows, it defines if the selected locale is metric or imperial, and the user has the option to override this setting. Software can read this value to determine what unit the user should have. – Is there a way for the browser to read this value? – Liggliluff Aug 31 '18 at 11:51

2 Answers2

2

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>
Professor Abronsius
  • 33,063
  • 5
  • 32
  • 46
1

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.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • 1
    This is pretty great, but would this require the user to reset the unity type every page they visit? – kevinaskevin May 27 '14 at 19:43
  • 1
    You could set a search string in the URL: `http://foo.com/whatever.html?units=metric` then just propagate the search string. Apply it at the server or client. – RobG May 27 '14 at 21:04