I'm beginning to learn OOP in Javascript and my instructor is not very good and I'm learning out of a very bad book. (http://www.amazon.com/JavaScript-The-Web-Technologies-Series/dp/0538748877/ref=cm_cr_pr_product_top) However I'm doing my best to use this site and any resource possible to follow along. That said! We're going over OOP and I'm trying to make a simple object oriented form validator but I'm having a problem with passing the input value to the method inside of the object. I apologize if I got the phrasing wrong. Here's my code.
function validate() {
this.isEmpty = function(value) {
if(value == "" || value.length < 1 || value == null) {
// testing alert
alert(value);
return false;
}
}
}
And my HTML
<form action="" method="get">
<label for="name">Name</label>
<input type="text" id="name" name="name">
<input type="submit" value="submit" id="submit">
</form>
Basically what I don't get is how I can pass the input value of the field "name" to the object? And my follow up to that would be how do I pass multiple inputs? For example if I wanted to check if "name" and another field named "email" were empty?
Can anyone shed some light on how I would go about doing this in regular JS? Am I even attempting this in the right way? I have no idea since this is my first time trying anything object oriented.
Well first it's important to know which concepts you are trying to model when doing OO. You were speaking about a "validator" concept, but perhaps it's still not specific enough. What exactly are you validating? Think about it, a "Validator" object suggests that it's actually quite flexible and is an object that could be helpful to validate many disparate models. If this isin't what you have in mind, then be more specific. For instance, you could name your class MyFormValidator (it's just an example).
Here's a very simple example of a specific validator class that takes a form as an argument and implements a public validate method to trigger validation on demand. The example is very simple and not quite flexible. There's a lot of space for improvements, such as removing UI concerns (like the messages), from the validator, but I did not want to make the example too complex.
*Note: _members identifies private members. You can enforce true privacy with the module pattern and/or priviledged functions, but I prefer naming conventions.*
function MyFormValidator(form) {
this.form = form;
this._errors = [];
}
MyFormValidator.prototype = {
constructor: MyFormValidator,
validate: function () {
var errors = this._errors,
name = this._valueOf('name');
//clear previous errors
errors.length = 0;
if (this._isEmpty(name)) {
errors.push('The name is mandatory');
}
return !errors.length;
},
errors: function () { return this._errors.slice() },
_valueOf: function (fieldName) {
return this.form.querySelector('[name="' + fieldName + '"]').value;
},
_isEmpty: function (value) {
return value == "" || value.length < 1 || value == null;
}
};
//Setting up form validation
var form = document.querySelector('form'),
validator = new MyFormValidator(form);
form.addEventListener('submit', function (e) {
if (!validator.validate()) {
e.preventDefault(); //prevent submit
alert(validator.errors());
}
});
DEMO: http://jsfiddle.net/Q2d5c/
When a JavaScript function is invoked inline, "this" points to the containing DOM element.
So if you want to validate the following tag:
<input name="test" onblur="validate();">
Then your validate function can grab its container's value:
function validate(){
alert(this.value); //the value of input field "test"
//to get the name of this input:
alert(this.name);
}
Personally I don't code like this because the code is sensitive to its context. Instead I use this style:
<input name="test" onblur="validate(this);">
function validate(d){
alert(d.value); //value
alert(d.name); //name
}
As you can see, the object is explicitly passed in, and you can see that on the call stack, "this" is placed in the tag directly. Later if you want to use the same function from another location, you can, by manually getting the object, and pass it in:
<input name="test" id="test">
<button onclick="validate(document.getElementById('test'));">Validate</button>
Unless it's an inline callback (closure) function, I avoid using the ambiguous "this" pointer to increase code readability.
I do not recommend to use a framework when learning javascript. After you learn javascript itself, then you should dive into frameworks, not now. So i will not tell anything about vanilla yet.
You can get values of inputs in several ways. The best is to get their value by id:
var name = document.getElementById("name").value
So there you have it, the value of the input assigned to name variable.
After that you can pass that variable to validate function and see the results.
You would also want to catch the form's submit event and do the validation before the form gets submitted. To do so:
var form = document.getElementsByID('form'); //you should assign id 'form' to the form element
form.addEventListener("submit", function(e) {
e.preventDefault(); //do not submit the form
var name = document.getElementById("name").value
validate(name);
});
It is simply done like that:
<script type="text/javascript">
function MsgAlert()
{
var value = document.getElementById('abc').value;
alert(value);
}
</script>
<input id="abc"></input>
<button onClick="MsgAlert();">PassValue</button>
Related
Consider the following example:
Suppose there is a object with enum property. If enum property has 5 value, the field value should be equal to 5, if enum is super object must have properties up and down. So the js code can be like that:
if (obj.enum === 5) {
if (obj.value != 5) {
//error here
}
}
if (obj.enum === 'super') {
if (obj.up === undefined || obj.down === undefined ) {
//error here
}
}
How to integrate this code into loopbackjs???
E.g. this is not a fields validation (custom or not), this it model validation. So isValid funciton (see below) and validate method can not be used. The validate validates only one property.
Here is very similar question but it ended up with custiom property validation.
Here is example with isValid funciton in validatable.js. Please note that this only invoking validation, there is no way to add additional errors or add additional validation
Use this answer as workaround. Pass one of always present fields to validate method and add custom error code and messages inside customValidator funciton.
I need some help writing a re-usable function for form checking. I want to call the function on an element of my form and perform a value validation; however I am not sure I have written the function correctly to accept the targeted element.
Here is a fiddle where you can find all the code.
My questions:
By using the element object as a parameter of the function, does this get the specified element when the user types in values in that input element?
And can I write the function in this way and then pass it into addEventListener?
I am trying to adapt the simple function found at this link.
The piece of JS code I am trying to get to work is as follows:
var A = {
invoiced: document.getElementById("invoiced"),
checkValue: function (event, elem) {
if(elem.validity.typeMismatch)
elem.setCustomValidity("Only number(s) and '+' and '-' signs");
else
elem.setCustomValidity("");
}
};
A.invoiced.addEventListener("keyup", A.checkValue, false);
checkValue: function(event) {
var elem = event.target || event.srcElement;
...
How may I write this jQuery function ( or rather, produce the same result ), using only JavaScript?
$(':input').serializeArray();
Function docs.
Look at jQuery's implementation:
function serializeArray() {
return this.map(function () {
// Can add propHook for "elements" to filter or add form elements
var elements = jQuery.prop(this, "elements");
return elements ? jQuery.makeArray(elements) : this;
}).filter(function () {
var type = this.type;
// Use .is(":disabled") so that fieldset[disabled] works
return this.name && !jQuery(this).is(":disabled") && rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && (this.checked || !manipulation_rcheckableType.test(type));
}).map(function (i, elem) {
var val = jQuery(this).val();
return val == null ? null : jQuery.isArray(val) ? jQuery.map(val, function (val) {
return {
name: elem.name,
value: val.replace(rCRLF, "\r\n")
};
}) : {
name: elem.name,
value: val.replace(rCRLF, "\r\n")
};
}).get();
}
Of course, this assumes that this is a jQuery object with methods like .filter() and .map(). These methods are also available to Arrays in ECMAScript 5, so if you don't need to support IE < 9, this code might work where this is an Array of HTMLElements — after removing or rewriting the corner cases that jQuery handles. If you need to support old browsers, then you probably should just use jQuery.
The jQuery :input selector is equivalent to:
var elements = document.querySelectorAll('input, button, textarea, select');
elements will be a static collection of the matched elements. A similar array could be built using getElementsByTagName using each different tag name.
.serializeArray creates an array of ojbects like:
[{name: value},{name:value},...];
To serialise elements, follow the algorithm in the HTML5 Spec §4.10.22 Form submission. Note that jQuery does not submit the submit button (which is counter to the W3C specification and browser behaviour), you may wish to emulate that (or not).
There is a good link in SajithNair's comment, it's not perfect but is a pretty good start (say 98% of the way there).
The basic strategy is to loop over the members and deal with each different type, creating objects from successful controls (i.e. those that have a name and value and aren't disabled, and are checked for radio and checkbox inputs and selected for options in selects, remembering to deal with multiple selects) whose sole member is the element name with a value of the element value.
Where there are multiple controls with the same, there will be multiple objects with a same named property but likely different value (or not).
The code to do that is not difficult, but it is a bit long and tedious to write and test thoroughly. Have a go and ask for help when you get stuck.
here is a fairly simple method that works for most forms:
[].slice.call(document.querySelectorAll('input[name]:not([disabled]), textarea[name]:not([disabled]), select[name]:not([disabled])')).reduce(function(a,b,i){
var val= String({checkbox:1,radio:1}[b.type] ? b.checked : b.value).replace(/\r?\n/g, "\r\n");
a[b.name]= a[b.name] ? a[b.name].concat(val) :val;
return a;
}, {});
it doesn't handle named submits or image types like a real form, but those input's applicability are really only known at time of clicking, and thus don't make sense to a manual serializer. You can use a couple of onclicks to emulate the traditional functionality for image inputs and named submits if that's really a need for you, which it likely isn't...
this version uses a flat object of key:value pairs, or key:[value1,value2] arrays for repeats.
Code version 1:
var one = "one";
$.each($(".Buttons"),(function() {
if ($("this:contains(one)")) {
$(this).addClass( "currentButton" );
};
}));
Code version 2:
var one = "one";
$(".Buttons").each(function(a, e) {
if ($("e:contains(one)"))
{ $(e).addClass( "currentButton" ); };
});
I think you see what I'm trying to do. My problem is only updating the specific element where the text is matched, but all elements are updated when only one matches.
Edit: HTML below:
<input type="submit" class="Buttons" value="one">
<input type="submit" class="Buttons" value="two">
<input type="submit" class="Buttons" value="one & two">
I am using inputs as they are programmatically added buttons using asp.net/c#
I have attempted a couple of solutions and I'm still having every element have the class added.
I updated this jsfiddle with inputs and now it's not being affected at all. I'm guessing :contains won't check input value.
Using my original method I fixed it as follows, or alternatively see the answer from Elias:
$(".Buttons").each(function() {
if ($(this).attr("value") == one) {
$(this).addClass("currentButton");
};
});
The easiest way to do what you're doing is a one-liner, though:
var one = "one";
$(".Buttons").filter(":contains('"+one+"')").addClass("currentButton");
//or
$(".Buttons").filter(":contains('one')").addClass("currentButton");
check fiddle
This does imply your :contains selector is a constant value, if you need it to change according to somethign else, wrap it in a function:
function changeClass(contains)
{
contains = contains || 'one';//default value
$('.Buttons').filter(":contains('"+contains+"')").addClass('currentButton');
}
Of course, as always, you can change everything to parameters so this function is more likely to be reusable:
function changeClass(selector, filter, newClass)
{
$(selector).filter(filter).addClass(newClass);
}
changeClass('.Buttons', ':contains("one")', 'currentButton');
Your problems were being caused by enclosing either this or e inside the string delimiters, which effectively turned them into string constants, not references to the DOM nodes you were trying to change
What happened is that both:
if ($("e:contains(one)"))
{
$(e).addClass( "currentButton" );
}
and
if ($("this:contains(one)"))
{
$(this).addClass( "currentButton" );
}
evaluated to :
if ([])
{
$(this).addClass('currentButton');
}
In other words: you were passing string constants to the main jQuery function ($()) which probably tried to make the bet of things, and likely treated them as selectors. Sadly, they came up empty, so an empty array-like jQuery object was returned, and any object/array is a truthy value in JS, so the expressions checked evalueted to true, hence, nodes classes were changed.
You could just as well have written:
if ($('foobar, see if it matters what I write here'))
{
console.log('It doesn\'t');
}
And it'll log It doesn't time and time again.
Edit
In response to your comment, if what you actually want is to filter, based on the elements' value attribute:
$('.Buttons').filter(function()
{//this function will be applied to each element returned by the $('.Buttons') selector
//only those for which this callback returns true will get the currentButton class
return /\bone\b/i.test($(this).val());
//or, equally valid:
return ($(this).val().indexOf('one') !== -1);
}).addClass('currentButton');
Note that /\bone\b/i will accept "ONe" as well as "One" or "one", but won't return true if the value attribute contains "bones", whereas indexOf is CaseSensitive, but doesn't check if one is part of a word or not.
for a case sensitive regex, you can use /\bone\b/, without the i.
A more strict, but lot shorter version of the same thing could be:
$('.Buttons[value~="'+one+'"]').addClass("currentButton");
But there are so many jQ selectors that you can use, best keep a reference close to your chest
DEMO
Using is():
var one = "one";
$(".Buttons").each(function () {
if ($(this).is(":contains('"+one+"')")) {
$(this).addClass("currentButton");
};
});
You can do this pretty simply without an each function. jQuery selectors will do the filtering for you, and then you update every element that matched the selector.
var text = "text to test for";
$(".Buttons:contains(" + text + ")").addclass("currentButton");
Here is a jsFiddle to demonstrate.
$("e:contains(one)") here one is being considered as a string literal.
try like, "e:contains('"+one+"')" now one will be evaluated to your string.
var one = "one";
$(".Buttons").filter(function () {
return $(":contains('"+one+"')",this);
}).addClass("currentButton");
or
var one = "one";
$(".Buttons").filter(function () {
return this.innerHTML == 'one'; // note: innerHTML may be deprecated soon, avoid it
}).addClass("currentButton");
jsFiddle demo
Ok noobablicious question. But has had me sumped.
Im declaring the value of a hidden form field with a Js script with in a function.
e.g. These are just examples not the real script.
function myFunction(){
var text = "hello world";
var number = 12345;
document.getElementById('text').value= text;
document.getElementById('number').value= number;
}
Then I want to be able to use the value of the form value as a variable in another script. I realize that there is the option to declare these variables globally. However I have heard that it is not as secure. Or a streamlined as I am going for.
Second Script example...
var autoText = document.getElementById('text').value;
var autoNumber = document.getElementById('number').value;
...do stuff with variables.
However this is not working and returns undefined. Is this the correct DOM path to access the value of my form fields or do I need to find an attribute and its child??
What other options are available to me??
Thanks for your time. HTML is...
<form action="http://mysite/mypath" method="post">
<input type="text" name="text" id="text" value="">
<input type="text" name="text" id="number" value="">
<input type= "submit" name="go" value="go" allign="middle"/>
</form>
That should be fine, assuming that you have the correct ID's set to the elements you want. Remember, that ID's are required to be unique, or unpredictable issues will arise.
Make sure that you are running your code, after the DOM is loaded. Otherwise the element might not yet exist in the DOM, and so the document.getElementById method will fail to find it..
Or, you could just store that data in a closure so that both functions have access to the variable, but it's not stored in the global scope. Like so:
(function(){
var text = "blah blah",
number = 12345;
function insertValues() {
document.getElementById('text').value= text;
document.getElementById('number').value= number;
}
function otherStuffWithValues() {
alert(text);
alert(number);
}
insertValues();
otherStuffWithValues();
}())
Additionally, You could also declare the variables inside the first function, and then pass the variables onto the second function as a parameter like so:
function insertValues() {
var text = "blah blah",
number = 12345;
document.getElementById('text').value= text;
document.getElementById('number').value= number;
otherstuff(text,number)
}
function otherstuff(sometext,somenumber) {
alert(sometext);
alert(somenumber);
}
insertValues()
I think that you haven't set starting script when page load. If so, you can use this simple event handler:
window.onload = myFunction;
With myFunction will be function with yours above code.