<div class="calc-outer-wrap">
<div id="form-error" style="display:none;"></div>
<form class="calc-form" id="calc-form">
<div class="form-fields">
<div class="field-outer col-1">
<label for="pace-per-mile" class="top-label">Pace Per Mile:</label>
<div class="field-items">
<div class="field">
<input type="text" name="per_mile_m" id="per-mile-m" inputmode="numeric" pattern="[0-9]*">
<label for="per-mile-m">min</label>
</div>
<!-- /.field -->
<div class="field">
<input type="text" name="per_mile_s" id="per_mile_s" inputmode="numeric" pattern="[0-9]*">
<label for="per-mile-s">sec</label>
</div>
<!-- /.field -->
</div>
<!-- /.field-items -->
</div>
<!-- /.field-outer -->
<div class="field-outer col-2">
<label for="r-pace-m" class="top-label">Pace Per Km:</label>
<div class="field-items">
<div class="field">
<input type="text" name="per_km_m" id="per-km-m" inputmode="numeric" pattern="[0-9]*">
<label for="per-km-m">min</label>
</div>
<!-- /.field -->
<div class="field">
<input type="text" name="per_km_s" id="per-km-s" inputmode="numeric" pattern="[0-9]*">
<label for="per-km-s">sec</label>
</div>
<!-- /.field -->
</div>
<!-- /.field-items -->
</div>
<!-- /.field-outer -->
<div class="field-outer col-1">
<label for="miles-per-h" class="top-label">Miles Per Hour:</label>
<div class="field-items">
<div class="field">
<input type="text" name="miles_per_h" id="miles-per-h" inputmode="numeric" pattern="[0-9].*">
</div>
<!-- /.field -->
</div>
<!-- /.field-items -->
</div>
<!-- /.field-outer -->
<div class="field-outer col-2">
<label for="km-per-h" class="top-label">Km Per Hour:</label>
<div class="field-items">
<div class="field">
<input type="text" name="km_per_h" id="km-per-h" inputmode="numeric" pattern="[0-9].*">
</div>
<!-- /.field -->
</div>
<!-- /.field-items -->
</div>
<!-- /.field-outer -->
<div class="btn-wrap">
<button class="form-submit">Calculate</button>
</div>
</div>
<!-- /.form fields -->
</form>
<div id="results" style="display:none;"></div>
</div>
<!-- /.calc-outer-wrap -->
$border-radius : 0.1875rem;
$primary: #9951FF;
$primary-dark: #803ede;
$secondary: #59e7ed;
$secondary-dark: darken($secondary, 10);
@mixin mobile {
@media (min-width:22.5rem) {
@content;
}
}
@mixin tablet {
@media (min-width:37.5rem) {
@content;
}
}
* {box-sizing: border-box;}
html, body {
margin: 0;
padding: 0;
}
.calc-outer-wrap {
margin: 0 auto;
background: #f3f3f3;
font-size: 1rem;
line-height: 1.3;
font-family: Helvetica Neue,Helvetica, Arial, sans-serif;
padding: 0.625rem;
}
.form-fields {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: top;
}
.field-outer {
width: 48%;
flex-basis: calc(50% - 5px);
&.col-large {
width: 60%;
flex-basis: calc(64% - 5px);
@include tablet {
width: 48%;
flex-basis: calc(50% - 5px);
}
}
@include mobile {
&:nth-child(n+3) {
margin-top: .5em;
}
}
}
label,
input,
select {
font-size: 1em;
line-height: 1.3;
display: block;
}
input,
select {
height: 1.75rem;
border: 1px solid #bbb;
width: 100%;
background: #fff;
padding: 0 0.325rem;
border-radius : $border-radius;
transition: box-shadow 200ms ease-in-out;
box-shadow: inset 0 0 0 0 $secondary;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
&:focus {
outline: 0;
box-shadow: inset 0 -3px 0 -1px $secondary;
border-radius: $border-radius $border-radius 1px 1px;
}
}
select {
font-size: 0.875rem;
background: #fff url("data:image/svg+xml;charset=UTF-8,%3csvg width='16px' height='14px' viewBox='0 0 8 17' xmlns='http://www.w3.org/2000/svg' fill='%238A8A8A'%3e%3cpolygon fill='%238A8A8A' points='4 17 0 10 8 10'%3e%3c/polygon%3e%3cpolygon fill='%238A8A8A' points='4 0 0 7 8 7'%3e%3c/polygon%3e%3c/svg%3e") right center no-repeat;
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
&::-ms-expand {
display: none;
}
}
.field-items {
display: flex;
justify-content: space-between;
label {
font-size: 0.75rem;
color: #999;
}
}
.field {
display: inline-block;
width: 32%;
flex-grow: 1;
+ .field {
margin-left: 3px;
}
}
.field-large {
flex: 1;
width: 67%;
}
.field-med.field-med {
width: 60%;
@include tablet {
width: 50%;
margin-left: 10px;
}
}
.field-small {
width: 4em;
flex-grow: 0;
}
.field-x-small {
width: 2em;
flex-basis: 2.4em;
flex-shrink: 0;
}
.top-label {
font-weight: bold;
font-size: 0.875rem;
line-height: 1.4;
display: block;
}
.btn-wrap {
align-self: flex-end;
margin: 1rem 0 0 auto;
margin-left: auto;
flex-basis: 12rem;
}
.form-submit {
background: $secondary;
display: block;
border: 0;
width: 100%;
font-size: 1rem;
border-radius: $border-radius;
height: 1.5rem;
color:#fff;
box-shadow: 1px 1px 1px 0 rgba(0,0,0,0.15);
cursor: pointer;
text-transform: uppercase;
@include mobile {
height: 2rem;
}
&:hover,
&:focus {
background: $secondary-dark;
box-shadow: 2px 2px 3px 0 rgba(0,0,0,0.3);
outline: 0;
}
&:active {
box-shadow: none;
}
}
.form-submit {
background: $secondary;
display: block;
border: 0;
width: 100%;
font-size: 1rem;
border-radius: $border-radius;
height: 1.5rem;
color:#fff;
box-shadow: 1px 1px 1px 0 rgba(0,0,0,0.15);
cursor: pointer;
text-transform: uppercase;
@include mobile {
height: 2rem;
}
&:hover,
&:focus {
background: $secondary-dark;
box-shadow: 2px 2px 3px 0 rgba(0,0,0,0.3);
outline: 0;
}
&:active {
box-shadow: none;
}
}
#form-error {
color: #fff;
margin-bottom: 0.5rem;
text-align:center;
color: #c1000b;
background-color: #ffd5d5;
padding: 3px;
font-size: 0.875rem;
}
#results {
position: relative;
width: 100%;
justify-content: center;
align-items: center;
font-size: 0.875rem;
@include mobile {
font-size: 1rem;
}
p {
margin: 0;
@include tablet {
text-align: center;
}
}
dl {
width: 22em;
margin: 1em auto 0;
column-count: 2;
dt,dd {
display: inline-block;
margin: 0;
}
dt {
width: 6em;
font-weight: bold;
}
dt,dd {
margin: 0;
}
dd {
width: 4em;
text-align: right;
}
}
#rs {
position: absolute;
bottom: 0;
right: 0;
font-size: 0.875rem;
color: #aaa;
&:hover {
color: $primary;
}
}
}
View Compiled
(function() {
const form = document.getElementById('calc-form');
const results = document.getElementById('results');
const errors = document.getElementById('form-error');
/**
* Display a form validation error
*
* @param {String} msg The validation message
* @return {Boolen} Returns false
*/
function errorMessage(msg) {
errors.innerHTML = msg;
errors.style.display = '';
return false;
}
/**
* Hide the results and reset the form
*/
function resetForm(e) {
if(e.target.id = 'rs') {
e.preventDefault();
results.style.display = 'none';
form.style.display = '';
form.reset()
}
}
/**
* Convert time to seconds
*
* @param {Int} h Hours
* @param {Int} m Minutes
* @param {Int} s Seconds
* @return {Int} The time in seconds
*/
function toSeconds(h, m, s) {
h = parseInt(h) || 0;
m = parseInt(m) || 0;
s = parseInt(s) || 0;
return h * 3600 + m * 60 + s;
}
/**
* Convert time to string formatted HH::MM::SS
*
* @param {Int} s The time in seconds
* @return {String} The time string
*/
function timeDisplay(s) {
let date = new Date(null);
date.setSeconds(s);
// Convert seconds to time string
let iso = date.toISOString().substr(11, 8);
// trim any leading zeroes or colons
return iso.replace(/^[:0]+/, '')
}
/**
* Display the results on the page
*
* @param {Int} options.avgPace The combined run/walk pace
* @param {Int} options.pacePerMi The combined run/walk pace in miles
* @param {String} options.unit The distance unit
*/
function displayResults({pacePerMile, pacePerKm, milesPerH, kmPerH}) {
results.innerHTML = `<p>Your pace is <b>${pacePerMile}</b> minutes per mile and <b>${milesPerH.toFixed(2)}</b> miles per hour<br>
(<b>${pacePerKm}</b> minutes per km and
<b>${kmPerH.toFixed(2)}</b> km per hour)
<a href="#" id="rs">reset</a>`
results.style.display = ''
form.style.display = 'none'
errors.style.display = 'none'
}
/**
* Handle form submit
* Gather/calculate data from form input and display results
*/
function handleSubmit(e) {
e.preventDefault();
let data = {
pacePerMile : toSeconds(0,form.per_mile_m.value, form.per_mile_s.value),
pacePerKm : toSeconds(0,form.per_km_m.value, form.per_km_s.value),
milesPerH: parseFloat(form.miles_per_h.value),
kmPerH: parseFloat(form.km_per_h.value)
}
if(!data.milesPerH == false) {
data.kmPerH = 1.60934 * data.milesPerH;
data.pacePerMile = timeDisplay(60/data.milesPerH*60);
data.pacePerKm = timeDisplay(60/data.kmPerH*60);
}
else if(!data.kmPerH == false) {
data.milesPerH = data.kmPerH / 1.60934;
data.pacePerMile = timeDisplay(60/data.milesPerH*60);
data.pacePerKm = timeDisplay(60/data.kmPerH*60);
}
else if(data.pacePerMile > 0) {
data.milesPerH = 60 / (data.pacePerMile / 60) ;
data.kmPerH = 1.60934 * data.milesPerH;
data.pacePerKm = timeDisplay(60/data.kmPerH*60);
data.pacePerMile = timeDisplay(data.pacePerMile);
}
else if(data.pacePerKm > 0) {
data.kmPerH = 60 / (data.pacePerKm / 60) ;
data.milesPerH = data.kmPerH / 1.60934;
data.pacePerMile = timeDisplay(60/data.milesPerH*60);
data.pacePerKm = timeDisplay(data.pacePerKm);
} else {
return errorMessage('Please enter one of the values below to convert your pace.')
}
console.log(data)
displayResults(data)
}
/**
* Keep unit select fields in sync
* Changing one changes both
*/
function handleChange(e) {
let sel = 'select.unit';
if(e.target.matches(sel)) {
let val = e.target.value;
document.querySelectorAll(sel).forEach(s => s.value = val);
}
// Hide errors
errors.style.display = 'none';
}
// Add Event Listeners
form.addEventListener('submit', handleSubmit);
form.addEventListener('change', handleChange);
results.addEventListener('click', resetForm, true);
})();
View Compiled
This Pen doesn't use any external CSS resources.