checkValidity / reportVality not working for values set via code - javascript

The HTML5 checkValidity() / reportValidity() methods don't seem to work if values are set via JavaScript.
Consider this example (JSFiddle):
<input id="text-field" maxlength="3" placeholder="Max len: 3 chars" />
<button id="set-field-value">Set</button>
<button id="check-valid">Is valid?</button>
<script>
window.onload = function() {
var textField = document.getElementById('text-field');
document.getElementById('set-field-value').onclick = function() {
textField.value = 'This is a very looooooooooooooooooooooooooooooong text';
};
document.getElementById('check-valid').onclick = function() {
window.alert(textField.checkValidity());
};
};
</script>
If you click the Set button, the value of the input field is set to an invalid value (it's length is greater that 3 characters), but the checkValidity() method still says that the input is valid (checked on Chrome, Edge and Firefox).
Why? Is there a way to determine is the field is valid even if its value is set via code?

I investigated this a while. It seems that you're at least missing <form> wrapping the input + button content.
But still, even if I tried to set input field required, still the ValidityObject won't notice that length is exceeded.
As a semi-workaround, I came with an idea of using pattern property:
<input id='textfield' pattern='\S{0,3}'>
(= \S stands for 'all characters except spaces')
This at least will prevent for content beyond three characters. In addition, you can have a setCustomValidity message for invalid cases.
See working example at: https://codepen.io/zvona/pen/rNavqxP?editors=1010

I've tried a few things that led me to understand that checkValidity does not check the value itself.
When I click on #check-validity I get :
textField.validity.tooLong // false
textField.validity.valid // true
textField.value // This is a very looooooooooooooooooooooooooooooong text
When I type myself in the input, my browser does not let me type more than 3 characters.
Why?
Precisely I don't know, but there is this awesome article on constraint validation that is worth having a look.
Is there a way to determine is the field is valid even if its value is
set via code?
textField.value returns your string, you can then access it's length. In this context, I would have anyway prefered this way.
const tooLong = textField.value.length > 3;
if (tooLong) window.alert('Something');
I hope it helps.

You should check if the form valid not the input. BUT it seems that the maxLength attribute is not something that trigger validation at all...
If you want to check length of input text you can do that with this:
window.onload = function() {
var textField = document.getElementById('text-field');
document.getElementById('set-field-value').onclick = function() { textField.value = 'ab'; };
document.getElementById('check-valid').onclick = function() {
if (textField.value && // if exist AND
textField.value.length > 2 && // if value have 3 charecter at least
textField.value.trim().length > 2 // if value is not just spaces
) {alert ('input OK');} // alert that input ok
else { alert('please insert at least 3 charecters');} // else alert error
};
};
<form id="formCheck">
<input type="text" id="text-field" min="3" />
<button type="button" id="set-field-value">Set</button>
<button type="button" id="check-valid">Is valid?</button>
</form>
The checkValidity() method works as expected on this example (using input number and min attribute) though:
window.onload = function() {
var theForm = document.getElementById('formCheck');
var numberField = document.getElementById('number-field');
document.getElementById('set-field-value').onclick = function() { numberField.value = '2'; };
document.getElementById('check-valid').onclick = function() {
window.alert(theForm.checkValidity());
};
};
<form id="formCheck">
<input type="number" id="number-field" min="3" />
<button type="button" id="set-field-value">Set</button>
<button type="button" id="check-valid">Is valid?</button>
</form>

Related

Why is setCustomValidity('') ignored on input (Chrome 65)

Note: to the best of my knowledge this question is not a duplicate question of the following:
HTML5: Why does my “oninvalid” attribute let the pattern fail?
HTML5 form required attribute. Set custom validation message?
How can I change or remove HTML5 form validation default error messages?
Overview
Given a field that:
Has pattern attribute set for validation, for example "[a-f,0-9]{4}" for a 4 character hex string input.
Has oninvalid set with setCustomValidity('...some message...') to define a custom validation message
Has oninput set with setCustomValidity('') to reset on input
Here is an example showing this:
/* jshint esnext: true */
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = field.value;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
event.target.setCustomValidity('must be valid 4 hex characters');
}
field.oninput = (event) => {
console.log("INPUT");
event.target.setCustomValidity('');
}
Output: <span id="output">No output</span>
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" autocomplete=off>
</form>
Validation works almost as desired, except when the user enters an invalid entry and then proceeds to try and edit it, where their following input states are still invalid:
At this point, neither the custom setCustomValidity message defined in oninvalid is used, nor the empty one defined in onInput.
Instead, as long as the field is in an invalid state and not blurred, the default Please match the requested format. message appears.
Question
What is going on here? Looking at the console, the oninput event is called each time, and therefore event.target.setCustomValidity(''); is called each time.
So why is it that we are still seeing the generic default validation message? Shouldn't setCustomValidity('') disable that?
An acceptable answer here should exhibit the following:
The parameter field is respected for validation.
Any validation message appears if and only if the user attempts to submit an invalid field and not when they modify the input immediately afterward.
The default Please match the requested format. message never appears at all.
It appears that this is a bug with Chrome 65 in windows.
using setCustomValidity('') in oninput should disable the default validation messages appearing on input.
The following workaround works for me:
/* jshint esnext: true */
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
const pattern = field.getAttribute("pattern");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = `User submitted: ${field.value}`;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
event.target.setCustomValidity('must be valid 4 hex characters');
}
field.oninput = (event) => {
console.log("INPUT");
event.target.setCustomValidity('');
event.target.removeAttribute("pattern");
}
field.onchange = (event) => {
console.log("CHANGE");
event.target.setAttribute("pattern", pattern);
}
Output: <span id="output">No output</span>
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" autocomplete=off>
</form>
setCustomValidity is meant to be used when multiple inputs, in combination are invalid. That is why it has to be reset to the empty string manually after. Other wise the title attribute should be used.
Trying to hide the validation error after editing the input is understandable but it is against the HTML5 form philosophy. It is meant to be shown as long as the input is invalid.
Adding maxlength can help the user to not cross the upper limit.
If you really want your bullet points to be satisfied feel free to not use HTML5 form validation but something custom instead.
So the reason a tooltip is shown even when setCustomValidity is set to empty string is because the input element is still invalid as per pattern attribute.
<form id="form">
<label for="field">Enter 4 character hex code: </label>
<input id="field" type="text" pattern="[a-f,0-9]{4}" maxlength="4" minlength="4" autocomplete="off" title="must be valid 4 hex characters">
</form>
JS
const form = document.querySelector("#form");
const field = document.querySelector("#field");
const output = document.querySelector("#output");
form.addEventListener('submit', (e) => {
console.log("SUBMIT");
output.textContent = field.value;
e.preventDefault(); // Prevent default POST request
});
field.oninvalid = (event) => {
console.log("INVALID");
}
field.oninput = (event) => {
console.log("INPUT");
}

Partial Password Masking on Input Field

So I need to mask a SSN# input field, lets say the ssn is 123-45-6789, I need to display ***-**-6789 (real time as they enter each digit) but I still need to retain the original value to submit.
I got to the point where I can do that if the user strictly enters the value but it breaks if the user does anything else such as delete, or moving cursor to a random position and adds/deletes a number, copy pasting/deleting, etc. I really don't want to listen to a bunch of events to make this work if thats even possible.
I also tried having a div sit on top of the input field to display the masked ssn while the actual ssn was transparent/hidden behind it but again they lose the functionality of being able to add/delete/select delete/paste in random parts (other then when they start at the end) and also the cursor not totally in sync with the end of the ssn number (asterisk size was the issue). This also broke on some mobile browsers.
I also thought of having two separate input fields, one type password, and one type text sit right next to each other, but again highlighting and deleting/pasting between the two would be an issue.
Ideally if there was something out there to have an input field have two types, part of the value be type password and the rest be type text, that would be fantastic. Btw this is for react js app.
TLDR: Need a fully functional input field that will do password masking on only first 5 digits of ssn and be plaintext for last 4 digits (while having the full plaintext value available for submission).
Thanks!
This might be a little sloppy, but it works as you want it to, is all in one text field, returns the full accurate SSN (despite replacing first 5 values with bullet points), and allows for editing anywhere in the field.
<input type="password" id="ssn" maxlength=9 />
<script>
var ssn = document.getElementById('ssn');
ssn.addEventListener('input', ssnMask, false);
var ssnFirstFive = "";
var secondHalf = "";
var fullSSN = "";
function ssnMask(){
if (ssn.value.length <= 5){
ssn.type = 'password';
}
else{
detectChanges();
secondHalf = ssn.value.substring(5);
ssn.type = 'text';
ssn.value = "•••••";
ssn.value += secondHalf;
fullSSN = ssnFirstFive + secondHalf;
}
}
function detectChanges() {
for (var i = 0; i < 5; i++){
if (ssn.value[i] != "•"){
ssnFirstFive = ssnFirstFive.substring(0, i) + ssn.value[i] + ssnFirstFive.substring(i+1);
}
}
}
</script>
Essentially, every time the input is changed, it checks to see if it matches the first 5 from before, and if it doesn't, it will update the necessary characters.
You can use 3 different fields and make then password fields.
Add a focus handler that changes their type into text and a blur handler that changes them back to password.
You can combine them before submission or on the server.
#ssn1{width:25px;}
#ssn2{width:20px;}
#ssn3{width:35px;}
<input type="password" name="ssn" maxlength=3 id="ssn1" />
<input type="password" name="ssn" maxlength=2 id="ssn2"/>
<input type="password" name="ssn" maxlength=4 id="ssn3"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$('[name="ssn"]').focus(function() {
$(this).attr("type", "text")
});
$('[name="ssn"]').blur(function() {
$(this).attr("type", "password")
});
</script>
You can also write a pass handler to all a full SSN to be pasted in the first field and have all three fields get set.
This is the closest you are going unless you work with a single full text box and give the user the ability to mask and unmask the field.
In production apps, this actually the approach I take:
Masked:
Unmasked:
You can implement you own focus/blur functions to automatically unmask/mask the field as needed.
Achieve this using html data attributes.
i have used the same html tag and store actual value in html tag attribute (data-value) to use later on and store value to display in html tag attribute value.
Function to partial mask input value
function mask_field_value(obj, mask_letters_count=7){
mask_value = $(this).data('mask-value') || '';
unmask_value = $(this).data('unmask-value') || '';
if (obj.value.length <= mask_letters_count){
obj.type = 'password';
mask_value = obj.value;
$(this).data('mask-value', obj.value);
} else {
obj.type = 'text';
unmask_value = obj.value.substring(mask_letters_count);
obj.value = "*".repeat(mask_letters_count) + unmask_value;
$(this).data('unmask-value', unmask_value);
}
$(this).data('value', mask_value + unmask_value);
console.log($(this).data('value'));
}
Add an event on input fields to mask
$(document).ready(function () {
$(document).on('keyup', '.mask_input_display', function () {
mask_field_value(this);
});
});

forcing focus to remain on a form text element until the value is numeric

I have a form which has input fields that expect numbers only.
I'm using javascript to validate the form when the value of the field changes.
If the value is numeric, do nothing.
If the value is not numeric, set it to zero and put focus in that text field. Essentially, I'm trying to trap the cursor in that field until a numeric value is entered. For some unknown reason, focus is not being placed on that form element. cell.focus() does not work. I've even tried document.getElementById(cel.getAttribute("ID")).focus(); What might I be doing wrong?
<html>
<head>
<script>
function NAN(cell){
if (cell.value != "") {
var re = /^(0|[1-9][0-9]*)$/;
if (re.test(cell.value) == false) {
alert('You must supply a numeric value greater than 0.');
cell.value = "0";
cell.focus();
}
}
}
</script>
</head>
<body>
<input type="text" name="num" value="" onchange="NAN(cell)"/>
</body>
</html>
Your problem is in the onchange attribute:
<input type="text" name="num" value="" onchange="NAN(cell)"/>
The value is executed as JavaScript code directly. You're passing code, not just a generic signature or prototype.
Inside those event handler snippets, there's a special object this defined, referring to the current DOM element (the input tag in this example).
(Just to mention it, there is also a second predefined object event, which most likely caused your confusion.)
As a simple fix for your issue, replace cell with this in the call and it should work:
<input type="text" name="num" value="" onchange="NAN(this)"/>
It's also important to note that you should keep in mind that this verification requires JavaScript to be executed. If it's disabled, the user might still pass any values, so you should check the value server side as well (assuming this isn't just client-only code).
As an alternative to using JavaScript, you could just use HTML5 to force a specific pattern on inputs. In this case this would be trivial to do:
<input type="text" name="num" value="" pattern="(?!0)\d+" title="Quantity">
The user won't be able to submit the form unless the pattern is validated, so there's no need to force the input focus. The pattern always has to match the full value, from beginning to the end. The title attribute is typically used to provide more information in the error popup.
There are two things done:
You have to change cell to this with onchange.
According to this question at least with Firefox setTimeout has to wrap this focus-method so that it works as expected.
And a more user-friendly approach is inserted as well at the second input-field.
Hope this example helps you:
function NAN(cell) {
if (cell.value != '') {
var re = /^(0|[1-9][0-9]*)$/;
cell.value = cell.value[0]=='0'?+cell.value:cell.value;
if (re.test(cell.value) == false) {
alert('You must supply a numeric value greater than 0.');
cell.value = '0';
setTimeout(function () {
cell.select();
cell.focus();
}, 0);
}
}
}
/*
* a more user friendly approach
*/
function NAN2(cell) {
if (cell.value != '') {
var re = /^(0|[1-9][0-9]*)$/;
cell.value = cell.value[0]=='0'?+cell.value:cell.value;
if (re.test(cell.value) == false) {
alert('You must supply a numeric value greater than 0.');
cell.value = '0';
setTimeout(function () {
cell.select();
cell.focus();
markElement(cell);
}, 0);
}
else{
tickElement(cell);
}
}
}
function tickElement(cell){
cell.setAttribute('style','border: 1px solid green');
}
function markElement(cell){
cell.setAttribute('style','border: 1px solid red');
}
<p>
Your approach(onchange):
<input type="text" name="num" value="" onchange="NAN(this)"/>
</p>
<p>
Or you can use a more user friendly approach to notify an user right now when they are tipping something wrong (onkeyup):
<input type="text" name="num" value="" onkeyup="NAN2(this)"/>
</p>

Javascript run onChange not onLoad

I have a very simple piece of Javascript that works perfectly onLoad, but I need it to work onChange.
My script;
<form action="" method="post" name="product_search">
<p><strong>Existing Part Number:</strong>
<input name="v_prodref" type="text" id="v_prodref" size="25" maxlength="25" onChange="searchValue()">
<input type="text" name="prodref" id="prodref">
<input type="submit" name="search_Submit" id="search_Submit" value="Submit">
</p>
<div>
<%=(rs_ProductCheck.Fields.Item("prodref").Value)%>
// <%=(rs_ProductCheck.Fields.Item("proddesc").Value)%></div>
<script>
function searchValue() {
var add = "NW";
var c_ProdRef = document.getElementById('v_prodref');
if(c_ProdRef.search(/GST/i) == -1) {
n_ProdRef = c_ProdRef.concat(add) }
else {
n_ProdRef = c_ProdRef.replace(/GST/i,"NWGST") }
document.getElementById("prodref").value = n_ProdRef;
}
</script>
</form>
So, I enter a part number in the first text box, and I want my javascript to run and enter the new value in the second text box, but it doesn't seem to work.
What am I missing?
search does not exist on an HTMLInputElement. You need to use c_ProdRef.value.search.
(Actually, since you're using it in many places as a string, and never as an input, you probably intended to define c_ProdRef as var document.getElementById('v_prodref').value)
You would've seen this error on load as well.
you want onkeyup if it works perfectly onLoad, and you want to start typing in something in textbox 1 and the javascript to run, you dont want onchange
onchange triggers after blur of focused element
onkeyup triggers after you release a keyboard input
Thanks to everyone for their help. After a little tweaking I have managed to get my code working.
function myFunction() {
var add = "NW";
var c_ProdRef = document.getElementById('v_prodref').value;
if (c_ProdRef.search(/GST/i) == -1) {
n_ProdRef = c_ProdRef.concat(add)
} else {
n_ProdRef = c_ProdRef.replace(/GST/i, "NWGST")
}
document.getElementById("prodref").value = n_ProdRef;
}
Along with #indubitablee suggestion of onKeyup and specifying the .value of my first text field it all works.

Custom Validation on HTML Number Input Misbehaving

In putting together a small webapp, I'm trying to ensure that end users are unable to place invalid characters in a number field that can hold signed floats. I'm using Dojo to search on an applied CSS class (in this case, ogInputNumber) and set events on input, keyup, and blur.
Ideally, I would like the input to be type="number" and to only allow digits, a hyphen (for signed floats), and a period character to act as a decimal place. If a user includes more than one hyphen or period character, the JS should truncate that second invalid character and everything thereafter in the input. Unfortunately, the JS behaves differently depending on whether the input is type="number" or type="text".
For type="text", if I attempt to enter the text 2.6a, 2.6 is fine, but the a is caught on the input event and prevented from appearing in the input. This is the desired behavior, but I would like to have the input as type="number" so the number spinners appear and for ease of use with mobile devices (so the number keyboard is brought up by default).
For type="number", if I attempt to enter the text 2.6a, the 2.6 is allowed to remain, but as soon as a is typed, the entire field is cleared out. That will prevent any invalid characters, but it's annoyingly overzealous. I've replicated this behavior on Chrome, Firefox, IE11, and Opera.
Can anyone offer any suggestions as to why the JS operates differently between inputs with type="text" and those with type="number"?
HTML:
<p>
<label for="numberInput1">Text Input</label>
<input id="numberInput1" class="ogInputNumber" type="text" />
</p>
<p>
<label for="numberInput2">Number Input</label>
<input id="numberInput2" class="ogInputNumber" type="number" />
</p>
JS:
// Checks number input fields for proper formatting
require(["dojo/domReady!", "dojo/on", "dojo/query"],
function (ready, on, query) {
query(".ogInputNumber").forEach(function (node) {
// Replace all the non-numeric, non-period, and non-hyphen characters with nothing while the user is typing
on(node, "input, keyup", function () {
this.value = this.value.replace(/[^\d\.-]/g, '');
});
// When the user leaves the input, format it properly as a signed float (or zero if it's something weird)
on(node, "blur", function () {
try {
if (this.value) {
this.value = parseFloat(this.value).toString();
} else {}
} catch (error) {
this.value = 0;
}
});
});
});
Working JSFiddle: http://jsfiddle.net/etehy6o6/1/
I think that's the default behavior of number input type, but I'm not sure. It's logical to think the input should not let the user put anything that is not a number, so it clears all the value before you can fire your keyup event.
So to keep the last valid value declare a variable outside the scope of your event and set it to the replaced value that was not cleared because invalid key input.
Using the code in your Fiddle:
Edited because addressed bug in comments
HTML
<!-- I asigned default values to test other scenarios -->
<p>
<label for="numberInput1">Text Input</label>
<input id="numberInput2" class="ogInputNumber" type="text" value="3.1416" />
</p>
<p>
<label for="numberInput">Number Input</label>
<input id="numberInput" class="ogInputNumber" type="number" value="3.1416" />
</p>
Javascript
// Checks number input fields for proper formatting
require(["dojo/domReady!", "dojo/on", "dojo/query"],
function (ready, on, query) {
query(".ogInputNumber").forEach(function (node) {
var validValue = this.value;
// Replace all the non-numeric, non-period, and non-hyphen characters with nothing while the user is typing
on(node, "input, keyup", function () {
if (this.value == '' && validValue.length > 1) {
this.value = validValue;
}
this.value = this.value.replace(/[^\d\.-]/g, '');
validValue = this.value;
});
// When the user leaves the input, format it properly as a signed float (or zero if it's something weird)
on(node, "blur", function () {
try {
if (this.value) {
this.value = parseFloat(this.value).toString();
} else {}
} catch (error) {
this.value = 0;
}
});
});
});

Categories

Resources