Library to add CSS classes to form inputs, describing their different states
window.formify = function formify(form, options) { | |
if (!(form instanceof HTMLFormElement)) { | |
throw new Error('Tried to Formify a non-form element: ' + form); | |
} | |
var opt = { | |
classPrefix: options.classPrefix || 'fm' | |
}; | |
var validityKeys = [ | |
'badInput', | |
'customError', | |
'patternMismatch', | |
'rangeOverflow', | |
'rangeUnderflow', | |
'stepMismatch', | |
'tooLong', | |
'tooShort', | |
'typeMismatch', | |
'valueMissing' | |
]; | |
var inputChangeHandlers = []; | |
Array.from(form.elements).forEach(function(el) { | |
inputChangeHandlers.push({ | |
el: el, | |
listener: setupFormElement(el) | |
}); | |
}); | |
listenInputChange(form, handleInputChange, true) | |
function handleInputChange(evt) { | |
inputChangeHandlers.forEach(function(handler) { | |
handler.listener(evt); | |
}); | |
} | |
function setupFormElement(el) { | |
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement || el instanceof HTMLButtonElement)) { | |
throw new Error('Tried to setup a non-input-ish element: ' + el); | |
} | |
var state = { | |
dirty: false, | |
touched: false, | |
valid: true, | |
empty: true, | |
invalidKeys: [], | |
}; | |
el.addEventListener('blur', changeStateFn('touched', true)); | |
listenInputChange(el, changeStateFn('dirty', true)); | |
var pTarget = el.getAttribute('data-formify-ptarget'); | |
var targets = [el]; | |
if (pTarget) { | |
var pIndex = parseInt(pTarget, 10); | |
var pEl = el; | |
for (var i = 0; i < pIndex; i++) { | |
pEl = el.parentNode; | |
} | |
targets.push(pEl); | |
} | |
updateValidity(); | |
return updateValidity; | |
function changeStateFn(stateName, val) { | |
return function() { | |
if (state[stateName] !== val) { | |
state[stateName] = val; | |
updateClasses(); | |
} | |
} | |
} | |
function updateValidity() { | |
var validity = el.validity; | |
state.empty = !el.value; | |
if (['checkbox', 'radio'].indexOf(el.type) !== -1) { | |
state.empty = !el.checked; | |
} | |
if (validity) { | |
state.valid = validity.valid; | |
state.invalidKeys = []; | |
validityKeys.forEach(function(key) { | |
if (validity[key]) { | |
state.invalidKeys.push(key); | |
} | |
}); | |
} | |
updateClasses(); | |
} | |
function updateClasses() { | |
targets.forEach(function(el) { | |
updateClass(state.dirty, '-dirty', '-pristine'); | |
updateClass(state.touched, '-touched', '-untouched'); | |
updateClass(state.valid, '-valid', '-invalid'); | |
updateClass(state.empty, '-empty', '-not-empty'); | |
function updateClass(val, trueClass, falseClass) { | |
if (val) { | |
el.classList.add(opt.classPrefix + trueClass); | |
el.classList.remove(opt.classPrefix + falseClass); | |
} else { | |
el.classList.add(opt.classPrefix + falseClass); | |
el.classList.remove(opt.classPrefix + trueClass); | |
} | |
} | |
validityKeys.forEach(function(key) { | |
if (state.invalidKeys.indexOf(key) === -1) { | |
el.classList.remove(opt.classPrefix + '-invalid-' + toKebabCase(key)); | |
} else { | |
el.classList.add(opt.classPrefix + '-invalid-' + toKebabCase(key)); | |
} | |
}); | |
}); | |
} | |
} | |
function listenInputChange(el, listener, useTarget) { | |
var handled = false; | |
var handleInputChange = function(evt) { | |
if (!handled) { | |
handled = true; | |
listener(evt); | |
setTimeout(function() { | |
handled = false; | |
}); | |
} | |
}; | |
if (useTarget) { | |
handled = []; | |
handleInputChange = function(evt) { | |
var target = evt.target; | |
if (handled.indexOf(target) === -1) { | |
handled.push(target); | |
listener(evt); | |
setTimeout(function() { | |
var index; | |
if ((index = handled.indexOf(target)) !== -1) { | |
handled.splice(index, 1); | |
} | |
}); | |
} | |
}; | |
} | |
el.addEventListener('input', handleInputChange); | |
el.addEventListener('change', handleInputChange); | |
} | |
function toKebabCase(str) { | |
return str.replace(/([A-Z])/g, function($1) { | |
return "-" + $1.toLowerCase(); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
If you're interested in a demo & explanation for this: http://bit.ly/formify-demo2