Reputation: 299
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
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
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 inches</span><span class="metric">25.4 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