I have an HTML5/Bootstrap form with hidden fields:
style="display: none"
which i show/hide via jQuery:
show() | hide()
For field validation i use the attribute required.
I want to have all my hidden fields as required but when some of them don't appear then the form can't proceed to submission.
Any thoughts about how can i have validation enabled only to fields displayed by user selections?
You can use this trick:
inside HTML form:
<input type="text" name="username" required="required" class="input-hidden">
CSS class:
.input-hidden{
height:0;
width:0;
visibility: hidden;
padding:0;
margin:0;
float:right;
}
You can add a class name for all the required attributes in the html:
<input type="text" name="first_name" class="card-payment-info" required>
<input type="text" name="last_name" class="card-payment-info" required>
and, from js event like a click, you can enable or disable the required attributes:
// disable require:
$(".card-payment-info").attr('required', false);
// enable require
$(".card-payment-info").attr('required', true);
Add a class .hideable to your hideable required inputs.
Then use these functions instead of your show() and hide():
function show1() {
//Your show() code here
$('.hideable').attr('required', 'required');
}
function hide1() {
//Your hide() code here
$('.hideable').removeAttr('required');
}
1 - Change the form to "novalidate"
2 - Catch the submit event
3 - Force the browser to check individually each visible input with input.reportValidity()
$('form')
.attr('novalidate', true)
.on('submit', function(){
var isValid = true;
$('input:visible,select:visible,textarea:visible', this).each(function() {
// report validity returns validity of input and display error tooltip if needed
isValid = isValid && this.reportValidity();
// break each loop if not valid
return isValid;
});
// do not submit if not valid
return isValid;
})
I've made a JQuery tool for this that also uses a mutation observer to automatically apply on dynamically created forms.
https://github.com/severinmoussel/VisibilityFormValidator/blob/master/VisibilityFormValidator.js
I wrote a drop-in replacement for the built-in show() and hide() that removes required on hide and restores them back on show (for all child elements).
(function ($) {
var oldShow = $.fn.show;
$.fn.show = function () {
oldShow.apply(this, arguments); //run the original "show" method
this.find("[notrequired]").prop("required", true).removeAttr("notrequired");
return this;
};
var oldHide = $.fn.hide;
$.fn.hide = function () {
oldHide.apply(this, arguments); //run the original "hide" method
this.find("[required]").prop("required", false).attr("notrequired", "1");
return this;
};
})(jQuery);
UPD: published as a gist here in case anyone wants to add anything https://github.com/alex-jitbit/jquery-required-visibility/blob/main/showhide.js
You can add pointer-events: none;, so the user can't click on the hidden element and also the cursor doesn't change when you hover it. As #DeadApe answered in this question
https://stackoverflow.com/a/65356856/6938902
One solution is to write your own validation-function:
This function checks all textareas and inputs in the default way, but first check if the input is displayed.
function validateFormOnlyVisableInputs(elForm) {
var inputs = elForm.querySelectorAll("input, textarea");
for (var i = 0; i < inputs.length; i++) {
console.log(i);
// display:none inputs ignorieren
if (inputs[i].offsetParent === null) continue;
if (!inputs[i].checkValidity()){
inputs[i].reportValidity();
return false;
}
}
return true;
}
You have to add an eventlistener to the submit-button and call the valdation function:
// this is out of a class, so you have to remove this...
// if you have problems you can write me ;)
this.elSendButton = this.elForm.querySelector("Button");
this.elSendButton.addEventListener("click", async (e) => {
e.preventDefault();
if (validateFormOnlyVisableInputs(this.elForm)) {
this.elForm.submit();
...
Related
I have created an typeahead on my input using angularjs ui-bootstrap. Code as below:
<div id="scrollable-dropdown-menu">
<input type="text" name="uName" ng-model="uName" autocomplete="off"
required class="form-control input-medium" placeholder="Enter user
name..." typeahead="uName.uName for uName in getUserNames($viewValue)"
typeahead-on-select='onSelect($item, $model, $label)'/>
</div>
I wanted to add a scroll to this so I have wrapped it around a div and added css to achieve scrolling.
The issue is if I start tying something and use my keyboards down arrow on the scroll I cant see the selected item ie the scroll does not move with the arrow key. I have to use my mouse to scrill. I believe its because I am setting a height of the div.
I have created a demo to show the issue as: https://codepen.io/kaka1981/pen/YOvYRY
Any solution for making this work ?
I was able to resolve this using below directive:
.directive('typeahead', function () {
return {
restrict: 'A',
priority: 1000, // Let's ensure AngularUI Typeahead directive gets initialized first!
link: function (scope, element, attrs) {
// Bind keyboard events: arrows up(38) / down(40)
element.bind('keydown', function (evt) {
if (evt.which === 38 || evt.which === 40) {
// Broadcast a possible change of the currently active option:
// (Note that we could pass the activeIdx value as event data but AngularUI Typeahead directive
// has its own local scope which makes it hard to retrieve, see:
// https://github.com/angular-ui/bootstrap/blob/7b7039b4d94074987fa405ee1174cfe7f561320e/src/typeahead/typeahead.js#L104)
scope.$broadcast('TypeaheadActiveChanged');
}
});
}
};
}).directive('typeaheadPopup', function () {
return {
restrict: 'EA',
link: function (scope, element, attrs) {
var unregisterFn = scope.$on('TypeaheadActiveChanged', function (event, data) {
if(scope.activeIdx !== -1) {
// Retrieve active Typeahead option:
var option = element.find('#' + attrs.id + '-option-' + scope.activeIdx);
if(option.length) {
// Make sure option is visible:
option[0].scrollIntoView(false);
}
}
});
// Ensure listener is unregistered when $destroy event is fired:
scope.$on('$destroy', unregisterFn);
}
};
});
Thanks to the post at: up/down arrow key issue with typeahead control (angular bootstrap UI)
Trying to implement 'required' attribute on IE<11 and Safari.
Here is the script I am using to try to accomplish this, but it isn't working. Looking for feedback or a slap in the face if necessary. The form id = "sendit"
$("#sendit").submit(function() {
if (!attributeSupported("required") || ($.browser.safari)) {
$("#sendit [required]").each(function(index) {
if (!$(this).val()) {
alert("Please fill all required fields.");
return false;
}
});
}
});
This is an old post, but I see it wasn't answered. The problem with your code is that you have the "return false" within the "each" function. That breaks out of the each loop but doesn't stop the form submit. I would just remove the browser checks since any browser that supports html5 form validation will pass the test. So you should be able to change the code to this:
$("#sendit").submit(function() {
var valid = true;
$("#sendit [required]").each(function(index) {
if (!$(this).val()) {
alert("Please fill all required fields.");
valid = false
$(this).focus();
return false;
}
});
if (!valid) {
return false;
}
// anything else that need to be done on submit
});
Note that I added a line to focus the form field as well.
I have select list in my html binded to observable array
<select data-bind="options: myData, optionsText: 'Name', value: selectedId"></select>
When i load my page i set value to observable selectedId
But when i set value it immediately calls its subscribe event
selectedId.subscribe(function (row) {
// some logic for retrieving data
});
I dont want subscribe event to call when value is being set programatically but only want to call when user selects something from list.
Is it possible to do? Cant find any good examples.
Update1
i set value to my observable selectedId in this way
selectedId(ko.utils.arrayFirst(data(), function(item) {
return item.id=== 5;
}));
I think because of this its firing subscribe event
If you set the initial value before you actually subscribe, then you shouldn't get a subscription event firing. For example:
function ViewModel() {
var self = this;
self.selectedID = ko.observable();
self.selectedID(10); // this won't fire the event because we haven't subscribed yet
self.selectedID.subscribe(function(value) {
alert("value changed by user");
});
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="number" data-bind="value: selectedID" />
Now if you want to solve the general case, where the value might change programmatically later and you don't want the subscribe to fire then, you might want to have a flag that you can use to simply skip the processing of the subscribe handler. Something like:
function ViewModel() {
var self = this;
self.selectedID = ko.observable();
self.selectedID(10); // this won't fire the event because we haven't subscribed yet
var _skipNotification = false; // this tells us whether or not we should process subscribe
// putting this logic in a function makes it easier if you have multiple places
// where you need to programmatically set the id.
function setSelectedID(value) {
_skipNotification = true;
self.selectedID(value);
_skipNotification = false;
}
self.selectedID.subscribe(function(value) {
if (!_skipNotification) {
alert("value changed by user");
}
});
self.changeProgrammatically = function() {
setSelectedID(1);
};
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="number" data-bind="value: selectedID" />
<input type="button" data-bind="click: changeProgrammatically" value="change programmatically" />
Here's your fiddle fixed to use the technique above. A local variable _skipNotification is defined in the view model and checked in the subscribe event. Then the importData function that is called from the mouse click looks like this:
this.importData = function () {
skipNotification = true;
self.selectedGroup(ko.utils.arrayFirst(self.availableGroups(), function (val) {
//debugger;
return val.GroupId == 8;
}));
skipNotification = false;
console.log("Lets see if it hits me first");
};
This will set the selectedGroup without causing the body of the selectedGroup.subscribe to execute. Note that as a result the selectedGroupId doesn't get set so your one span will still say You have chosen Nothing despite the fact that something is selected in the drop-down. I'm not sure if that was what you were actually going for, but it seems pretty misleading. Especially since the only way to now get it to correctly read You have chosen Football Team is to select something else first and then reselect Football Team.
I'm new to Mootools and I have found that I have to use the click element but I'm not 100% sure where I am meant to put it in the below code:
function setInStockOption (labels, e) {
active = false;
labels.some (function (item,index) {
if(item.hasClass ('selected')) {
if(item.hasClass ('unavailable')) {
item.removeClass('selected');
item.addClass ('unselected');
active = true;
} else {
return true;
}
}
if(active) {
if (!item.hasClass ('unavailable')) {
e.target = this;
item.fireEvent ('click', e);
active = false;
return true;
}
}
});
}
window.addEvent('load', function(e) {
var labels = $$('div.option-radios label.radio');
setInStockOption(labels, e);
});
I basically need to add the class selected on click instead. At the moment this script is adding the selected class to the first child of Radio in the html and then when you click on others it'll add the class selected. I basically want all the classes to be unselected when the page loads
.
Any ideas?
You'll want something like this:
window.addEvent('domready', function(e) {
$$('div.option-radios label.radio').each(function(label, i) {
label.addEvent('click', function(event) {
event.target.toggleClass('selected');
});
});
});
Note that this uses the Array.each method instead of Array.some. The latter doesn't do what you expect. It then registers a click event on every label which simply toggles the selected class on the event target.
Then you can add other initialization code to the each loop and more logic to the click event handler.
I also used the domready event which is usually preferred over load since it fires earlier.
Here's a fiddle to play around with.
I have a text input element that I want user to enter city or zip code and I want to trigger a javascript function as the user types text. My html is as follows
<input type="text"
placeholder="Zip Code Or City Name"
class="input-block-level"
data-bind="value: searchterm, valueUpdate: 'afterkeydown', event: { keyup: getZipCodes }" />
in my viewmodel I have
var getZipCodes = function () {
if (searchterm().length < 2) {
return;
}
datacontext.getZips(searchterm(), zipcodes);
return true;
};
But it turns out that the getZipCodes is never invoked.
I tried data-binding to "onkeyup" but no go, I am sure there is some subtle error I am making. Can someone help me find what I am doing wrong?
I tried changing getZipCodes to getZipCodes = function(data, event) {...} but that does not work either. The function just does not get invoked.
By the way I am trying to do pretty much same as Knockout event binding for input keypress causes weird behavior
but does not work for me.
After changing the definition of getZipCodes as follows it started working.
function getZipCodes(data, event) {
if (searchterm().length < 2) {
return true;
}
datacontext.getZips(searchterm(), zipcodes);
return true;
};
Instead of
var getZipCodes = function()
if I use
function getZipCodes()....
it works, not really sure what the difference is.