Stop chrome auto filling / suggesting form fields by id - html

Ok so...like many other posts this is driving me nuts. Chrome is continually offering autocomplete suggestions for fields that I would rather it not be on. It, along with the soft keyboard take up the whole page which blocks the view for the user / the form is not intended to fill our the users data but rather a new address that would be previously unknown.
So far I've got these both on
<form autocomplete="off">
and
<input autocomplete="randomstringxxx">
Their effect is noticed and chrome is no longer filling the whole form - but it STILL wants to suggest single field suggestions for each element in my form.
I've finally realised that its now picking up the id/name fields from my form elements.
i.e the below will give me a list of names I have used before.
<input id="contact_name" name="contact_name">
Can anyone suggest a way to stop this without renaming the elements? They are tied to fields in my database and ideally I would not have to manually rename and match up these together.
example -
https://jsfiddle.net/drsx4w1e/
with random strings as autocomplete element attribute - STILL AUTOCOMPLETING
https://jsfiddle.net/drsx4w1e/1/
with "off" as autocomplete attribute. - STILL AUTOCOMPLETING
https://jsfiddle.net/6bgoj23d/1/
example no autocomplete when labels / ids/ name attr are removed - NOT AUTOCOMPLETING
example

I know this isn't ideal because it changes the name of the inputs but it only does it temporarily. Changing the name attribute is the only way I found that completely removes the autocomplete.
This solution is all in JS and HTML but I think it would be better if it was implemented with a server side language such as PHP or Java.
I found autocomplete="none" works best for chrome but it doesn't fully turn off auto complete.
How it works
So, on page load this solution adds a string of random characters to each input name.
eg. 'delivery_contact_name' becomes 'delivery_contact_nameI5NTE'
When the form is submitted it calls a function (submission()) which removes the random character that were added. So the submitted form data will have the original names.
See solution below:
<html>
<body>
<form autocomplete="none" id="account_form" method="post" action="" onsubmit="return submission();">
<div class="my-2">
<label for="delivery_contact_name" class="">*</label>
<input autocomplete="none" class="form-control" id="delivery_contact_name" maxlength="200" minlength="2" name="delivery_contact_name" required="" type="text" value="">
</div>
<div class="my-2">
<label for="delivery_telephone" class="">Telephone*</label>
<input autocomplete="none" class="form-control" id="delivery_telephone" maxlength="200" minlength="8" name="delivery_telephone" required="" type="tel" value="">
</div>
<div class="my-2">
<label for="delivery_address_1" class="">Delivery Address*</label>
<input autocomplete="none" class="form-control" id="delivery_address_1" maxlength="50" minlength="2" name="delivery_address_1" required="" type="text" value="">
</div>
<div class="my-2">
<label for="delivery_address_2" class="">Delivery Address*</label>
<input autocomplete="none" class="form-control" id="delivery_address_2" maxlength="50" minlength="2" name="delivery_address_2" required="" type="text" value="">
</div>
<div class="my-2">
<label for="delivery_address_3" class="">Delivery Address</label>
<input autocomplete="none" class="form-control" id="delivery_address_3" name="delivery_address_3" type="text" value="">
</div>
<div class="my-2">
<label for="delivery_address_4" class="">Delivery Address</label>
<input autocomplete="none" class="form-control" id="delivery_address_4" name="delivery_address_4" type="text" value="">
</div>
<div class="my-2">
<label for="delivery_address_postcode" class="">Delivery Postcode*</label>
<input autocomplete="none" class="form-control" id="delivery_address_postcode" maxlength="10" minlength="6" name="delivery_address_postcode" required="" type="text" value="">
</div>
<input type="submit" name="submit" value="Send">
</form>
</body>
<script>
//generate a random string to append to the names
const autocompleteString = btoa(Math.random().toString()).substr(10, 5);
//get all the inputs in the form
const inputs = document.querySelectorAll("input");
//make sure script calls function after page load
document.addEventListener("DOMContentLoaded", function(){
changeInputNames();
});
//add random characters to input names
function changeInputNames(){
for (var i = 0; i < inputs.length; i++) {
inputs[i].setAttribute("name", inputs[i].getAttribute("name")+autocompleteString);
}
}
//remove the random characters from input names
function changeInputNamesBack(){
for (var i = 0; i < inputs.length; i++) {
inputs[i].setAttribute("name", inputs[i].getAttribute("name").replace(autocompleteString, ''));
}
}
function submission(){
let valid = true;
//do any additional form validation here
if(valid){
changeInputNamesBack();
}
return valid;
}
</script>
</html>

Thanks to #rydog for his help. I've changed it into a function that I've put into a my js file as I didn't want to manually add to each page / fire on every page - I have also added the submit event handler with js rather than adding to the on submit of the form.
GREAT SOLUTION by Rydog
function stop_autofill() {
//generate a random string to append to the names
this.autocompleteString = btoa(Math.random().toString()).substr(10, 5);
this.add_submit_handlers = () => {
document.querySelectorAll("form").forEach(value => {
value.addEventListener("submit", (e) => {
this.form_submit_override(e)
})
})
}
//add random characters to input names
this.changeInputNames = () => {
for (var i = 0; i < this.input_elements_arr.length; i++) {
this.input_elements_arr[i].setAttribute("name", this.input_elements_arr[i].getAttribute("name") + this.autocompleteString);
}
}
//remove the random characters from input names
this.changeInputNamesBack = () => {
for (var i = 0; i < this.input_elements_arr.length; i++) {
this.input_elements_arr[i].setAttribute("name", this.input_elements_arr[i].getAttribute("name").replace(this.autocompleteString, ''));
}
}
this.form_submit_override = (e) => {
e.preventDefault()
this.changeInputNamesBack()
e.currentTarget.submit()
return true
}
this.setup_form = () => {
//get all the inputs in the form
this.input_elements_arr = document.querySelectorAll("input");
this.changeInputNames();
this.add_submit_handlers();
}
//make sure script calls function after page load
this.init = () => {
if (document.readyState === "complete") {
this.setup_form()
} else {
let setup_form = this.setup_form
document.addEventListener("DOMContentLoaded", function (e) {
setup_form()
})
}
}
}
on the page that needs it
<script>
af = new stop_autofill()
af.init()
</script>

Related

How to have 2 textboxes as mutually exclusive in an html form?

I would like to have 2 mutually exclusive fields.
One will be a FileField and other TextBoxField.
Is there a ready html form I can get my hands on to.
I have searched the web and couldnt find any.
Oh I am a little sorry..
I meant that I wanted to do this via Django Templates
You can make an onInput event listener and handle it using javascript, so that if the user types in one field it empties the other.
For example:
<form>
<label for="first">Fill This:</label>
<input type="text" name="first" id="first" oninput="run('first')"><br><br>
<label for="second">Or This:</label>
<input type="text" name="second" id="second" oninput="run('second')"><br><br>
<input type="submit" value="Submit">
</form>
<script>
function run(activeField) {
if (activeField == 'first') {
const second = document.querySelector('#second')
second.value = ''
} else {
const first = document.querySelector('#first')
first.value = ''
}
}
</script>
For Your textbox, you can use this:
<input type="text" name="name" placeholder="Please enter your name">
And for your files:
<input type="file" name="fileName">
But for file name it needs to be encrypted. HTML won't let you submit a form with a file. But you can override this in the form attr, like this:
<form action="dirToForm.py" method="POST" enctype="multipart/form-data"></form>

Using javascript to create a for loop which loops over text fields, and concatenates the input, and has an add input button

This program has 6 text fields and when a user inputs into the text fields, the text result box will concatenate the input text. I am struggling to get a button to work which will add a 7th text field and then also add the user input together. I have tried to append it but not sure where I am going wrong.
<html>
<body>
<form>
<div class="textFields">
<label for="text1">text1:</label><br>
<input type="text" class="text" name="text1"><br>
<label for="text2">text2:</label><br>
<input type="text" class="text" name="text2"><br>
<label for="text3">text3:</label><br>
<input type="text" class="text" name="text3"><br>
<label for="text4">text4:</label><br>
<input type="text" class="text" name="text4"><br>
<label for="text5">text5</label><br>
<input type="text" class="text" name="text5"><br>
<label for="text6">text6</label><br>
<input type="text" class="text" name="text6"><br>
<input type="button" name="button" value="Get"><br>
<input type="button" name="button" value="Add">
<br>
<label for="textResult">Text Result</label><br>
<input type="text" id="textResult" name="textResult"><br>
</div>
</form>
<script>
let x = document.querySelectorAll('.textFields .text');
let button = document.querySelector('.textFields input[type="button"]');
let result = document.querySelector('#textResult');
button.onclick = function() {
result.value = '';
for (i = 0; i < x.length; i++) {
result.value += x[i].value + ' ';
}
}
button.onclick = function() {
var textField = document.createElement("INPUT")
textField.setAttribute("id", id)
textField.setAttribute("name", id)
textField.classList.add("textInput")
container.appendChild(textField)
}
</script>
</body>
</html>
When you run this in a browser, the following error is reported in the console when you click the Get button:
Uncaught ReferenceError: id is not defined
at HTMLInputElement.button.onclick (test.html:53)
Ignore the error for now, you can see that the error in actually in the second function, but for Get you were probably expecting the first funtion. To fix this issue, do not assign the second function, at least not to the button you have selected.
Notice how you have named both buttons the same name, this will make them hard to target, but also you are not using that name in the querySelector. So lets change that first, give each button a unique name and use it to select each button:
<input type="button" name="getButton" value="Get"><br>
<input type="button" name="addButton" value="Add">
let getButton = document.querySelector('.textFields input[name="getButton"]');
let addButton = document.querySelector('.textFields input[name="addButton"]');
...
getButton.onclick = ...
...
addButton.onclick = ...
Now, when you click on the Get button there is no error, and it appears to function as you have described, clicking Add still raises the original error.
You have used a variable called id but you have not yet declared what that variable is yet. I would assume you probably want to make it 'textX' where x is the next number.
So add the following lines inside the button click function to declare the Id:
You need to put this logic inside the function because you need it to be re-evaluated each time the button is clicked. Other valid solutions would include incrementing the value instead or re-querying for x, but this will work.
let x = document.querySelectorAll('.textFields .text');
let id = 'text' + (x.length + 1);
Save and Run, you will see the next issue in the console:
Uncaught ReferenceError: container is not defined
at HTMLInputElement.addButton.onclick
As with id, you have not defined the variable container, here I will again assume you meant to reference the .textFields div, so following your querySelector style, we can create a variable called container:
let container = document.querySelector('.textFields');
That will start appending your text boxes to the page, but they are still not being picked up by the Get button.
Another assumption here, but you have assigned a class .textResult to the new texboxes. If instead you assigned the class .text to them, then you would almost pick them up in the selector
textField.classList.add("text");
The reason that they aren't picked up is back to where the value of x is evaluated that the Get button is using. Because it is evaluated the first time in the main script, but never re-evaluated when the button is clicked the new text boxes are not included in the array stored in x.
As with the advice above for requerying x to get the updated count, Simply fix this by moving the line to initialise x into the first function.
Overall, your page with the embedded script could not look something like this:
<html>
<body>
<form>
<div class="textFields">
<label for="text1">text1:</label><br>
<input type="text" class="text" name="text1"><br>
<label for="text2">text2:</label><br>
<input type="text" class="text" name="text2"><br>
<label for="text3">text3:</label><br>
<input type="text" class="text" name="text3"><br>
<label for="text4">text4:</label><br>
<input type="text" class="text" name="text4"><br>
<label for="text5">text5</label><br>
<input type="text" class="text" name="text5"><br>
<label for="text6">text6</label><br>
<input type="text" class="text" name="text6"><br>
<input type="button" name="getButton" value="Get"><br>
<input type="button" name="addButton" value="Add">
<br>
<label for="textResult">Text Result</label><br>
<input type="text" id="textResult" name="textResult"><br>
</div>
</form>
<script>
let getButton = document.querySelector('.textFields input[name="getButton"]');
let addButton = document.querySelector('.textFields input[name="addButton"]');
let result = document.querySelector('#textResult');
let container = document.querySelector('.textFields');
getButton.onclick = function() {
let x = document.querySelectorAll('.textFields .text');
result.value = '';
for (i = 0; i < x.length; i++) {
result.value += x[i].value + ' ';
}
}
addButton.onclick = function() {
let x = document.querySelectorAll('.textFields .text');
var textField = document.createElement("INPUT")
let id = 'text' + (x.length + 1);
textField.setAttribute("id", id)
textField.setAttribute("name", id)
textField.classList.add("text")
container.appendChild(textField)
}
</script>
</body>
</html>
Have a look at some of the guidance in this post for further simple examples: How do I add textboxes dynamically in Javascript?

Select a random element from a dynamic array in HTML

I'd like to add HTML to a Google Site that allows a user to press a button that displays a random letter of the alphabet. However, it should randomize only the letters that the user selects through checkboxes. Below is an image of what I'd like to achieve, and I'd like the result to display to the right of the checkbox array.
As to what I have tried so far, I have the following code that I modified from an open source online. I hope it is ok for my purpose.
<!DOCTYPE html>
<html>
<body>
<h1>Pick Letters To Randomize</h1>
<form action="/action_page.php">
<input type="checkbox" id="letter1" name="letter1" >
<label for="letter1"> A</label><br>
<input type="checkbox" id="letter2" name="letter2" >
<label for="letter2"> B</label><br>
<input type="checkbox" id="letter3" name="letter3" >
<label for="letter3"> C</label><br><br>
<input type="submit" value="Randomize">
</form>
</body>
</html>
But I am really at a loss for how to solve the rest of my problem.
Here is a working example for you. I have a few suggestions that I've implemented that will make this easier for you:
Add a value to the checkbox input. That way, you don't have to grab a child/sibling label.
I've added comments to show what I'm doing. Hope that helps!
document.addEventListener("DOMContentLoaded", function() {
const form = document.getElementById("randomLetterForm");
const submitBtn = document.getElementById("randomSubmit");
const textResult = document.getElementById("result");
// We check the values on the submit click
submitBtn.addEventListener("click", function(e) {
// Prevent it from *actually* submitting (e.g. refresh)
e.preventDefault();
// Grab *all* selected checkboxed into an array
const items = document.querySelectorAll("#randomLetterForm input:checked");
// Checking if it's not empty
if (items.length > 0) {
// Setting a random index from items[0] to items[items.length]
textResult.innerHTML = items[Math.floor(Math.random() * items.length)].value;
} else {
// If not, we alert
alert("Please choose at least 1 number");
}
});
});
<h1>Pick Letters To Randomize</h1>
<form id="randomLetterForm" action="/action_page.php">
<input type="checkbox" value="A" id="letter1" name="letter1" >
<label for="letter1"> A</label><br>
<input type="checkbox" value="B" id="letter2" name="letter2" >
<label for="letter2"> B</label><br>
<input type="checkbox" value= "C" id="letter3" name="letter3" >
<label for="letter3"> C</label><br><br>
<input id="randomSubmit" type="submit" value="Randomize">
</form>
<div>
<p id="result"></p>
</div>

minimum one required for checkboxes with same name [duplicate]

When using the newer browsers that support HTML5 (FireFox 4 for example);
and a form field has the attribute required='required';
and the form field is empty/blank;
and the submit button is clicked;
the browsers detects that the "required" field is empty and does not submit the form; instead browser shows a hint asking the user to type text into the field.
Now, instead of a single text field, I have a group of checkboxes, out of which at least one should be checked/selected by the user.
How can I use the HTML5 required attribute on this group of checkboxes?
(Since only one of the checkboxes needs to be checked, I can't put the required attribute on each and every checkbox)
ps. I am using simple_form, if that matters.
UPDATE
Could the HTML 5 multiple attribute be helpful here? Has anyone use it before for doing something similar to my question?
UPDATE
It appears that this feature is not supported by the HTML5 spec: ISSUE-111: What does input.#required mean for #type = checkbox?
(Issue status: Issue has been marked closed without prejudice.)
And here is the explanation.
UPDATE 2
It's an old question, but wanted to clarify that the original intent of the question was to be able to do the above without using Javascript - i.e. using a HTML5 way of doing it. In retrospect, I should've made the "without Javascript" more obvious.
Unfortunately HTML5 does not provide an out-of-the-box way to do that.
However, using jQuery, you can easily control if a checkbox group has at least one checked element.
Consider the following DOM snippet:
<div class="checkbox-group required">
<input type="checkbox" name="checkbox_name[]">
<input type="checkbox" name="checkbox_name[]">
<input type="checkbox" name="checkbox_name[]">
<input type="checkbox" name="checkbox_name[]">
</div>
You can use this expression:
$('div.checkbox-group.required :checkbox:checked').length > 0
which returns true if at least one element is checked.
Based on that, you can implement your validation check.
Its a simple trick. This is jQuery code that can exploit the html5 validation by changing the required properties if any one is checked. Following is your html code (make sure that you add required for all the elements in the group.)
<input type="checkbox" name="option[]" id="option-1" value="option1" required/> Option 1
<input type="checkbox" name="option[]" id="option-2" value="option2" required/> Option 2
<input type="checkbox" name="option[]" id="option-3" value="option3" required/> Option 3
<input type="checkbox" name="option[]" id="option-4" value="option4" required/> Option 4
<input type="checkbox" name="option[]" id="option-5" value="option5" required/> Option 5
Following is jQuery script, which disables further validation check if any one is selected. Select using name element.
$cbx_group = $("input:checkbox[name='option[]']");
$cbx_group = $("input:checkbox[id^='option-']"); // name is not always helpful ;)
$cbx_group.prop('required', true);
if($cbx_group.is(":checked")){
$cbx_group.prop('required', false);
}
Small gotcha here: Since you are using html5 validation, make sure you execute this before the it gets validated i.e. before form submit.
// but this might not work as expected
$('form').submit(function(){
// code goes here
});
// So, better USE THIS INSTEAD:
$('button[type="submit"]').on('click', function() {
// skipping validation part mentioned above
});
HTML5 does not directly support requiring only one/at least one checkbox be checked in a checkbox group. Here is my solution using Javascript:
HTML
<input class='acb' type='checkbox' name='acheckbox[]' value='1' onclick='deRequire("acb")' required> One
<input class='acb' type='checkbox' name='acheckbox[]' value='2' onclick='deRequire("acb")' required> Two
JAVASCRIPT
function deRequireCb(elClass) {
el = document.getElementsByClassName(elClass);
var atLeastOneChecked = false; //at least one cb is checked
for (i = 0; i < el.length; i++) {
if (el[i].checked === true) {
atLeastOneChecked = true;
}
}
if (atLeastOneChecked === true) {
for (i = 0; i < el.length; i++) {
el[i].required = false;
}
} else {
for (i = 0; i < el.length; i++) {
el[i].required = true;
}
}
}
The javascript will ensure at least one checkbox is checked, then de-require the entire checkbox group. If the one checkbox that is checked becomes un-checked, then it will require all checkboxes, again!
I guess there's no standard HTML5 way to do this, but if you don't mind using a jQuery library, I've been able to achieve a "checkbox group" validation using webshims' "group-required" validation feature:
The docs for group-required say:
If a checkbox has the class 'group-required' at least one of the
checkboxes with the same name inside the form/document has to be
checked.
And here's an example of how you would use it:
<input name="checkbox-group" type="checkbox" class="group-required" id="checkbox-group-id" />
<input name="checkbox-group" type="checkbox" />
<input name="checkbox-group" type="checkbox" />
<input name="checkbox-group" type="checkbox" />
<input name="checkbox-group" type="checkbox" />
I mostly use webshims to polyfill HTML5 features, but it also has some great optional extensions like this one.
It even allows you to write your own custom validity rules. For example, I needed to create a checkbox group that wasn't based on the input's name, so I wrote my own validity rule for that...
we can do this easily with html5 also, just need to add some jquery code
Demo
HTML
<form>
<div class="form-group options">
<input type="checkbox" name="type[]" value="A" required /> A
<input type="checkbox" name="type[]" value="B" required /> B
<input type="checkbox" name="type[]" value="C" required /> C
<input type="submit">
</div>
</form>
Jquery
$(function(){
var requiredCheckboxes = $('.options :checkbox[required]');
requiredCheckboxes.change(function(){
if(requiredCheckboxes.is(':checked')) {
requiredCheckboxes.removeAttr('required');
} else {
requiredCheckboxes.attr('required', 'required');
}
});
});
Inspired by the answers from #thegauraw and #Brian Woodward, here's a bit I pulled together for JQuery users, including a custom validation error message:
$cbx_group = $("input:checkbox[name^='group']");
$cbx_group.on("click", function () {
if ($cbx_group.is(":checked")) {
// checkboxes become unrequired as long as one is checked
$cbx_group.prop("required", false).each(function () {
this.setCustomValidity("");
});
} else {
// require checkboxes and set custom validation error message
$cbx_group.prop("required", true).each(function () {
this.setCustomValidity("Please select at least one checkbox.");
});
}
});
Note that my form has some checkboxes checked by default.
Maybe some of you JavaScript/JQuery wizards could tighten that up even more?
I added an invisible radio to a group of checkboxes.
When at least one option is checked, the radio is also set to check.
When all options are canceled, the radio is also set to cancel.
Therefore, the form uses the radio prompt "Please check at least one option"
You can't use display: none because radio can't be focused.
I make the radio size equal to the entire checkboxes size, so it's more obvious when prompted.
HTML
<form>
<div class="checkboxs-wrapper">
<input id="radio-for-checkboxes" type="radio" name="radio-for-required-checkboxes" required/>
<input type="checkbox" name="option[]" value="option1"/>
<input type="checkbox" name="option[]" value="option2"/>
<input type="checkbox" name="option[]" value="option3"/>
</div>
<input type="submit" value="submit"/>
</form>
Javascript
var inputs = document.querySelectorAll('[name="option[]"]')
var radioForCheckboxes = document.getElementById('radio-for-checkboxes')
function checkCheckboxes () {
var isAtLeastOneServiceSelected = false;
for(var i = inputs.length-1; i >= 0; --i) {
if (inputs[i].checked) isAtLeastOneCheckboxSelected = true;
}
radioForCheckboxes.checked = isAtLeastOneCheckboxSelected
}
for(var i = inputs.length-1; i >= 0; --i) {
inputs[i].addEventListener('change', checkCheckboxes)
}
CSS
.checkboxs-wrapper {
position: relative;
}
.checkboxs-wrapper input[name="radio-for-required-checkboxes"] {
position: absolute;
margin: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-appearance: none;
pointer-events: none;
border: none;
background: none;
}
https://jsfiddle.net/codus/q6ngpjyc/9/
I had the same problem and I my solution was this:
HTML:
<form id="processForm.php" action="post">
<div class="input check_boxes required wish_payment_type">
<div class="wish_payment_type">
<span class="checkbox payment-radio">
<label for="wish_payment_type_1">
<input class="check_boxes required" id="wish_payment_type_1" name="wish[payment_type][]" type="checkbox" value="1">Foo
</label>
</span>
<span class="checkbox payment-radio">
<label for="wish_payment_type_2">
<input class="check_boxes required" id="wish_payment_type_2" name="wish[payment_type][]" type="checkbox" value="2">Bar
</label>
</span>
<span class="checkbox payment-radio">
<label for="wish_payment_type_3">
<input class="check_boxes required" id="wish_payment_type_3" name="wish[payment_type][]" type="checkbox" value="3">Buzz
</label>
<input id='submit' type="submit" value="Submit">
</div>
</form>
JS:
var verifyPaymentType = function () {
var checkboxes = $('.wish_payment_type .checkbox');
var inputs = checkboxes.find('input');
var first = inputs.first()[0];
inputs.on('change', function () {
this.setCustomValidity('');
});
first.setCustomValidity(checkboxes.find('input:checked').length === 0 ? 'Choose one' : '');
}
$('#submit').click(verifyPaymentType);
https://jsfiddle.net/oywLo5z4/
You don't need jQuery for this. Here's a vanilla JS proof of concept using an event listener on a parent container (checkbox-group-required) of the checkboxes, the checkbox element's .checked property and Array#some.
const validate = el => {
const checkboxes = el.querySelectorAll('input[type="checkbox"]');
return [...checkboxes].some(e => e.checked);
};
const formEl = document.querySelector("form");
const statusEl = formEl.querySelector(".status-message");
const checkboxGroupEl = formEl.querySelector(".checkbox-group-required");
checkboxGroupEl.addEventListener("click", e => {
statusEl.textContent = validate(checkboxGroupEl) ? "valid" : "invalid";
});
formEl.addEventListener("submit", e => {
e.preventDefault();
if (validate(checkboxGroupEl)) {
statusEl.textContent = "Form submitted!";
// Send data from e.target to your backend
}
else {
statusEl.textContent = "Error: select at least one checkbox";
}
});
<form>
<div class="checkbox-group-required">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
</div>
<input type="submit" />
<div class="status-message"></div>
</form>
If you have multiple groups to validate, add a loop over each group, optionally adding error messages or CSS to indicate which group fails validation:
const validate = el => {
const checkboxes = el.querySelectorAll('input[type="checkbox"]');
return [...checkboxes].some(e => e.checked);
};
const allValid = els => [...els].every(validate);
const formEl = document.querySelector("form");
const statusEl = formEl.querySelector(".status-message");
const checkboxGroupEls = formEl.querySelectorAll(".checkbox-group-required");
checkboxGroupEls.forEach(el =>
el.addEventListener("click", e => {
statusEl.textContent = allValid(checkboxGroupEls) ? "valid" : "invalid";
})
);
formEl.addEventListener("submit", e => {
e.preventDefault();
if (allValid(checkboxGroupEls)) {
statusEl.textContent = "Form submitted!";
}
else {
statusEl.textContent = "Error: select at least one checkbox from each group";
}
});
<form>
<div class="checkbox-group-required">
<label>
Group 1:
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
</label>
</div>
<div class="checkbox-group-required">
<label>
Group 2:
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
</label>
</div>
<input type="submit" />
<div class="status-message"></div>
</form>
I realize there are a ton of solutions here, but I found none of them hit every requirement I had:
No custom coding required
Code works on page load
No custom classes required (checkboxes or their parent)
I needed several checkbox lists to share the same name for submitting Github issues via their API, and was using the name label[] to assign labels across many form fields (two checkbox lists and a few selects and textboxes) - granted I could have achieved this without them sharing the same name, but I decided to try it, and it worked.
The only requirement for this one is jQuery, which could easily be eliminated if you wanted to rewrite it in vanilla JS. You can combine this with #ewall's great solution to add custom validation error messages.
/* required checkboxes */
jQuery(function ($) {
var $requiredCheckboxes = $("input[type='checkbox'][required]");
/* init all checkbox lists */
$requiredCheckboxes.each(function (i, el) {
//this could easily be changed to suit different parent containers
var $checkboxList = $(this).closest("div, span, p, ul, td");
if (!$checkboxList.hasClass("requiredCheckboxList"))
$checkboxList.addClass("requiredCheckboxList");
});
var $requiredCheckboxLists = $(".requiredCheckboxList");
$requiredCheckboxLists.each(function (i, el) {
var $checkboxList = $(this);
$checkboxList.on("change", "input[type='checkbox']", function (e) {
updateCheckboxesRequired($(this).parents(".requiredCheckboxList"));
});
updateCheckboxesRequired($checkboxList);
});
function updateCheckboxesRequired($checkboxList) {
var $chk = $checkboxList.find("input[type='checkbox']").eq(0),
cblName = $chk.attr("name"),
cblNameAttr = "[name='" + cblName + "']",
$checkboxes = $checkboxList.find("input[type='checkbox']" + cblNameAttr);
if ($checkboxList.find(cblNameAttr + ":checked").length > 0) {
$checkboxes.prop("required", false);
} else {
$checkboxes.prop("required", true);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form method="post" action="post.php">
<div>
Type of report:
</div>
<div>
<input type="checkbox" id="chkTypeOfReportError" name="label[]" value="Error" required>
<label for="chkTypeOfReportError">Error</label>
<input type="checkbox" id="chkTypeOfReportQuestion" name="label[]" value="Question" required>
<label for="chkTypeOfReportQuestion">Question</label>
<input type="checkbox" id="chkTypeOfReportFeatureRequest" name="label[]" value="Feature Request" required>
<label for="chkTypeOfReportFeatureRequest">Feature Request</label>
</div>
<div>
Priority
</div>
<div>
<input type="checkbox" id="chkTypeOfContributionBlog" name="label[]" value="Priority: High" required>
<label for="chkPriorityHigh">High</label>
<input type="checkbox" id="chkTypeOfContributionBlog" name="label[]" value="Priority: Medium" required>
<label for="chkPriorityMedium">Medium</label>
<input type="checkbox" id="chkTypeOfContributionLow" name="label[]" value="Priority: Low" required>
<label for="chkPriorityMedium">Low</label>
</div>
<div>
<input type="submit" />
</div>
</form>
Really simple way to verify if at least one checkbox is checked:
function isAtLeastOneChecked(name) {
let checkboxes = Array.from(document.getElementsByName(name));
return checkboxes.some(e => e.checked);
}
Then you can implement whatever logic you want to display an error.
Here is another simple trick using Jquery!!
HTML
<form id="hobbieform">
<div>
<input type="checkbox" name="hobbies[]">Coding
<input type="checkbox" name="hobbies[]">Gaming
<input type="checkbox" name="hobbies[]">Driving
</div>
</form>
JQuery
$('#hobbieform').on("submit", function (e) {
var arr = $(this).serialize().toString();
if(arr.indexOf("hobbies") < 0){
e.preventDefault();
alert("You must select at least one hobbie");
}
});
That's all.. this works because if none of the checkbox is selected, nothing as regards the checkbox group(including its name) is posted to the server
Pure JS solution:
const group = document.querySelectorAll('[name="myCheckboxGroup"]');
function requireLeastOneChecked() {
var atLeastOneChecked = false;
for (i = 0; i < group.length; i++)
if (group[i].checked)
atLeastOneChecked = true;
if (atLeastOneChecked)
for (i = 0; i < group.length; i++)
group[i].required = false;
else
for (i = 0; i < group.length; i++)
group[i].required = true;
}
requireLeastOneChecked(); // onload
group.forEach(function ($el) {
$el.addEventListener('click', function () { requireLeastOneChecked(); })
});
Hi just use a text box additional to group of check box.When clicking on any check box put values in to that text box.Make that that text box required and readonly.
A general Solution without change the submit event or knowing the name of the checkboxes
Build a Function, which marks the Checkbox as HTML5-Invalid
Extend Change-Event and check validity on the start
jQuery.fn.getSiblingsCheckboxes = function () {
let $this = $(this);
let $parent = $this.closest('form, .your-checkbox-listwrapper');
return $parent.find('input[type="checkbox"][name="' + $this.attr('name')+'"]').filter('*[required], *[data-required]');
}
jQuery.fn.checkRequiredInputs = function() {
return this.each(function() {
let $this = $(this);
let $parent = $this.closest('form, .your-checkbox-list-wrapper');
let $allInputs = $this.getSiblingsCheckboxes();
if ($allInputs.filter(':checked').length > 0) {
$allInputs.each(function() {
// this.setCustomValidity(''); // not needed
$(this).removeAttr('required');
$(this).closest('li').css('color', 'green'); // for debugging only
});
} else {
$allInputs.each(function() {
// this.reportValidity(); // not needed
$(this).attr('required', 'required');
$(this).closest('li').css('color', 'red'); // for debugging only
});
}
return true;
});
};
$(document).ready(function() {
$('input[type="checkbox"][required="required"], input[type="checkbox"][required]').not('*[data-required]').not('*[disabled]').each(function() {
let $input = $(this);
let $allInputs = $input.getSiblingsCheckboxes();
$input.attr('data-required', 'required');
$input.removeAttr('required');
$input.on('change', function(event) {
$input.checkRequiredInputs();
});
});
$('input[type="checkbox"][data-required="required"]').checkRequiredInputs();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
</head>
<form>
<ul>
<li><input type="checkbox" id="checkbox1" name="countries" value="Argentina" required="required">Argentina</li>
<li><input type="checkbox" id="checkbox2" name="countries" value="France" required="required">France</li>
<li><input type="checkbox" id="checkbox3" name="countries" value="Germany" required="required">Germany</li>
<li><input type="checkbox" id="checkbox4" name="countries" value="Japan" required="required">Japan</li>
<li><input type="checkbox" id="checkbox5" name="countries" value="Australia" required="required">Australia</li>
</ul>
<input type="submit" value="Submit">
</form>
Try:
self.request.get('sports_played', allow_multiple=True)
or
self.request.POST.getall('sports_played')
More specifically:
When you are reading data from the checkbox array, make sure array has:
len>0
In this case:
len(self.request.get('array', allow_multiple=True)) > 0

HTML5 required attribute one of two fields

I have a form with two required input fields:
<form>
<input type="tel" name="telephone" required>
<input type="tel" name="mobile" required>
<input type="submit" value="Submit">
</form>
Is it possible to get browsers to validate so only one of them is required? i.e if telephone is filled, don't throw an error about mobile being empty and vice versa
Update 2020-06-21 (ES6):
Given that jQuery has become somewhat unfashionable in the JavaScript world and that ES6 provides some nice syntactic sugar, I have written a pure JS equivalent to the original answer:
document.addEventListener('DOMContentLoaded', function() {
const inputs = Array.from(
document.querySelectorAll('input[name=telephone], input[name=mobile]')
);
const inputListener = e => {
inputs
.filter(i => i !== e.target)
.forEach(i => (i.required = !e.target.value.length));
};
inputs.forEach(i => i.addEventListener('input', inputListener));
});
<form method="post">
Telephone:
<input type="tel" name="telephone" value="" required>
<br>Mobile:
<input type="tel" name="mobile" value="" required>
<br>
<input type="submit" value="Submit">
</form>
This uses the input event on both inputs, and when one is not empty it sets the required property of the other input to false.
Original Answer (jQuery):
I played around with some ideas and now have a working solution for this problem using jQuery:
jQuery(function ($) {
var $inputs = $('input[name=telephone],input[name=mobile]');
$inputs.on('input', function () {
// Set the required property of the other input to false if this input is not empty.
$inputs.not(this).prop('required', !$(this).val().length);
});
});
I've written a jQuery plugin wrapping the above JavaScript code so that it can be used on multiple groups of elements.
Based on Andy's answer, but I needed a checkbox implementation & came up with this.
what role(s) do you want?
<input type="checkbox" data-manyselect="roler" name="author" required>
<input type="checkbox" data-manyselect="roler" name="coder" required>
<input type="checkbox" data-manyselect="roler" name="teacher" required>
where will you work?
<input type="checkbox" data-manyselect="placement" name="library" required>
<input type="checkbox" data-manyselect="placement" name="home" required>
<input type="checkbox" data-manyselect="placement" name="office" required>
jQuery(function ($) {
// get anything with the data-manyselect
// you don't even have to name your group if only one group
var $group = $("[data-manyselect]");
$group.on('input', function () {
var group = $(this).data('manyselect');
// set required property of other inputs in group to false
var allInGroup = $('*[data-manyselect="'+group+'"]');
// Set the required property of the other input to false if this input is not empty.
var oneSet = true;
$(allInGroup).each(function(){
if ($(this).prop('checked'))
oneSet = false;
});
$(allInGroup).prop('required', oneSet)
});
});
Here for anyone else getting here by googling and wanting a quick solution for one of many checkboxes.
You would better do form data validation with Javascript anyway, because the HTML5 validation doesn't work in older browsers. Here is how:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form Validation Phone Number</title>
</head>
<body>
<form name="myForm" action="data_handler.php">
<input type="tel" name="telephone">
<input type="tel" name="mobile">
<input type="button" value="Submit" onclick="validateAndSend()">
</form>
<script>
function validateAndSend() {
if (myForm.telephone.value == '' && myForm.mobile.value == '') {
alert('You have to enter at least one phone number.');
return false;
}
else {
myForm.submit();
}
}
</script>
</body>
</html>
.
Live demo here: http://codepen.io/anon/pen/LCpue?editors=100. Let me know if this works for you, if you will.
For two text fields #Andy's answer is working awesome, but in case of more than two fields we can use something like this.
jQuery(function ($) {
var $inputs = $('input[name=phone],input[name=mobile],input[name=email]');
$inputs.on('input', function () {
var total = $('input[name=phone]').val().length + $('input[name=mobile]').val().length + $('input[name=email]').val().length;
$inputs.not(this).prop('required', !total);
});
});