Understanding and correcting the structure of Javascript code - javascript

so I've been on here for awhile, and I'm still considered an entry level programmer based on my general knowledge of structure and basic concepts. I have a function below that was given to me in an answer for a different question I asked. I can understand most of what it is doing, but I need help understanding the rest of what it does. I'm asking this because I would really like to understand further advanced concepts of javascript, and jQuery.
So what I've done below is placed the function, and I'll comment in what I know about what the function is doing at where, and then I'll place question marks where I"m confused.
function validate(){
//array of objeccts used to defined the class selector for each element iterated
//with what validation function is be assigned to that specific selector
var fields = [
{
selector: $('.not-empty'),
validations: [ isNotEmpty]
},
{
selector: $('.email'),
validations: [ isNotEmpty, isEmail]
},
{
selector: $('.number'),
validations: [ isNotEmpty, isNumber]
},
{
selector: $('.number-noreq'),
validations: [isNumberNotRequired]
},
{
selector: $('.checked'),
validations: [isChecked]
}
];
//remove any classes of 'has-error' from each element traversed before validation begins
$('.form-control').closest('.form-group').removeClass('has-error');
//defining variables
var i = 0, k = 0, z = 0, j = fields.length, item, selector, fn, info;
//for loop to traverse the fields array of objects
for(; i < j; i++){
item = fields[i];
//traversing each field.validation
for(k = 0; k < item.validations.length; k++){
fn = item.validations[k]; //setting fn as a function found in validation
//traversing each selector in item
for( z = 0; z < item.selector.length; z++){
selector = $(item.selector[z]); //setting the selector
//attempting to set info to the closest form or input group found by the selector
info = selector.closest('.form-group, .input-group');
if(info) //if info contains data
//?????????????????????????????????????? no idea what's going on below other
//other than it's running the validation function that was passed, but why
//is it written like this and what is it doing?
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
}
}
}
}
So that is the basic question I have for this code (where all the question marks are). If someone can clearly answer what is going on, why you write the code like that, what the purpose of it is, and is it benefcial or not, would be fantastic. if you need more clarification I would be happy to provide it. I just want to be able to explain the code to somebody and know what I am talking about instead of trying to have to bs my through it. I think it was Einstein who said, "If you can't explain something accurately and to the point, then you truly do not understand it" or something like that!
Thank you in advance!
EDIT: here are the functions that 'validations' traverse through
//validation functions
function isNotEmpty(value){
return value && $.trim(value).length > 0;
}
function isEmail(value){
return /^([^#\s\t\n]+\#[\w\d]+\.[\w]{2,3}(\.[\w]{2})?)$/.test(value);
}
function isNumber(value){
return /^\d+$/.test(value);
}
function isNumberNotRequired(value){
return /^\d+$/.test(value) || value.length < 1;
}
function isChecked(value){
var r = false;
var name = $(value).attr('name');
$('input[name="'+name+'"').each(function(){
if($(this).is(':checked')){
r = true;
}
});
return r;
}
SECOND EDIT/UPDATE: We have determined that there is a severe error in the code that allows it not to keep track of the validation and take into account previous validations for input groups, and other related sections. How does this corrected. I'm testing items on jsfiddle at the moment I will return when I have restuls!

This line:
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
is equivalent to this:
var result = fn(selector.val());
if (result)
info.removeClass("has-error");
else
info.addClass("has-error");
How is that? Well, your code calls the function plucked from the list of validation routines stored in that data structure, passing the value of the field to be tested. The result of that function call is used as a true/false test in the ? : expression. If the result is true, the ? : resolves to the string "removeClass"; if false, to "addClass".
Now, what is info? It's a jQuery object that refers to the closest piece of the DOM that (presumably) is where an error message would be displayed, or where some other indicator would be shown based on some CSS rule. The [ ] operator will take whichever of those two strings the ? : resolves to and use that as a property accessor. The net effect, therefore, is to reference either info.removeClass or info.addClass. Those are both references to jQuery methods, so one or the other will be called. In either case, the code wants to operate on the class name "has-error", because it wants to either add it (when the validation fails) or remove it (when the validation succeeds).
That said, the code has a serious defect: if, for a given field, there is in fact a list of validation functions, the code will run all of them (which is fine). However, for each validation function, it sets or clears that "has-error" class without regard to prior validation results. That might work, if you're really careful with the ordering of the validation functions, but that's an awfully fragile way of doing things. I think it would be much more robust if it made each test and kept track of whether any test failed, and then after that process is complete for a given field it'd only then set or clear the "has-error" class.
Fixing the code isn't too hard. Currently it iterates the the validation functions outside the iteration over the selected fields, which (I think) is backwards. However, as long as it checks the state of the error indicator element(s), it should be OK.
First, at the top, the code removes "has-error" from .form-group elements but not from .input-group elements. That's clearly incorrect, so:
$('.form-control').closest('.form-group, .input-group').removeClass('has-error');
Then, in the loop:
for( z = 0; z < item.selector.length; z++){
selector = $(item.selector[z]); //setting the selector
//attempting to set info to the closest form or input group found by the selector
info = selector.closest('.form-group, .input-group');
if (info.length && !fn(selector.val())) // if info contains data and field is invalid
info.addClass('has-error');
}
Since all the "has-error" flags are cleared at the outset, all we need to do is add the class to classes that are invalid. If you wanted to have a positive "is-ok" class, then you'd add that to everything at the top and remove it when you find an error.

As you should have known, foo.bar are foo["bar"] are identical in JavaScript (if you did not know, learn it, now).
This line
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
means
var methodName;
if (fn(selector.val())) { methodName = 'removeClass'; } else { methodName = 'addClass'; }
info[methodName]('has-error')
so, in yet another words,
if (fn(selector.val())) {
info.removeClass('has-error');
} else {
info.addClass('has-error');
}
So it is actually switching class has-error on/off. Just it's pretty densely written.

Related

Native JS alternative for Java's next() and hasNext() methods

So recently I built a search and replace program with Java, now I am working on translating/rebuilding that program with JavaScript. However, I am having trouble finding JS method alternatives for next() and hasNext(). I am new to JS so I don't know what JS methods would work similarly to the Java methods I am used to.
This is my program, I commented through it to show exactly what I am doing with the previously mentioned methods. Basic set up, 2 text areas, one for the search box (search criteria, box 2), and one for the main document (the field of search, box 1). It basically boils down to a cross-reference. It will highlight all the similarities between the documents.
function search() {
//define an array to store the search criteria.
var array = [];
// define a counter.
var n = 0;
// define a constant for the first box, the search field.
const box1 = document.getElementById("box1");
// define a constant for the second box, the search criteria.
const box2 = document.getElementById("box2");
// loop through the search criteria, storing each word as a seperate element in the array.
// this uses non js terms, this is where I need the help.
while (box2.hasNext()) {
array[n] = box2.next();
n = n + 1;
}
// resets the counter.
n = 0;
// loops through each search item, finding and replacing each item with itself, surrounded by mark tags.
while (n <= array.length) {
box1.replace(array[n], "<mark>" + array[n] + "</mark>");
}
}
</script>
There is bound to be other issues, bugs and syntax, feel free to point them out but lets try and keep the focus on the methodology (i.e. method alternatives for next() and hasNext()).
Thanks.
-EDIT- I'd prefer to use native alternative (no jquery) becuase I know even less about jquery than I do js.

javascript: setting default value with IF statement instead of OR

I'm only sharing a small bit of code because there is so much going on, and I hope this is enough to answer my question.
I have some existing JS where a value is determined with an OR statement and I think I need to convert that to an IF statement. The final output is currently giving me both values if they both exist, and I only want "question" where both "question" and "name" values exist.
var question = new fq.Question(questionData.answerId, topicId,
questionData['question'] || questionData['name'],
questionData['text']);
Instead of using the OR operator (answerData['question'] || answerData['name']), I'd like to do something similar to the following:
if (questionData['question'] is undefined) {
use questionData['question'];
} else {
use instead questionData['name']
}
But, I don't know how I might accomplish such a statement within the () in the existing code pasted above. The name variable/value is always present, so there's no risk in defaulting to that. Question is only defined some of the time. And I don't ever want both appearing.
This is probably outside of the scope of my query here, but to fill in a little more detail, this code eventually outputs JSON files for topics and questions. Topics only have names values, and questions have both names and questions, but I only want the questions json to include questions values, not names. I'm pretty sure this is the key part in all of the JS to determin
Create a function and get value from there.
Need to remember scope of function:
Example Snippet:
var that = this;
var question = new fq.Question(questionData.answerId, topicId,
that.getValue(),
questionData['text']);
function getValue() {
if (questionData['question']) { //null and undefined both are false
return questionData['question']
} else {
return questionData['name']
}
}

onkeydown, and auto complete

I was wondering if anyone could help me solve this issue or point me towards the right direction.
In my project we have a filed that needs to be autofilled, at this moment I use onblur which works wonders as it only does it so once you leave the focus. However, due to recent changes, it needs to only do so when there is only one unique item in the map which it matches the input.
I have a large array defined as following:
var myArray = [
[content, content],
[content, content],
...
]
Later in my code I associate it with a map, at least this is what most stackoverflow questions I looked at referred to it as follows:
var myMap = {};
for(0 to myArray.length) {
var a = myArray[i][0];
var b = myArray[i][1];
myMap[a] = b;
}
Now, finally I iterate over this array as follows:
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0)
the above is the line of code I am struggling to figure out how to change. At this moment, while using on blur, if I type in the letter 'A' for example, and leave the focus area it will automatically fill it in with a certain name. However, in the array there are many other objects that begin with, or contain A. How can I change it so that the onkeydown event will keep going until it finally filters it down to to only possible key-value pair? I tried looking at MDN's documentation for filtering, but I do not think that will work for my purposes, or at least I am too inexperienced with JS.
If the indexOf the first and last are nonnegative and equal, there is just one. You could do this with an && and boolean short circuit evaluation, but that will run very far right off the screen, so I am showing your code with one more nested if (up to you to add the end of the block). But we also need to see if there are matches on multiple keys.
var matchCount=0;
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0){
if (map[key].toLowerCase().indexOf(location.toLowerCase()) == map[key].toLowerCase().lastIndexOf(location.toLowerCase())) {
matchCount++;
then outside your for loop:
if (matchCount==1){ //do your stuff

Form handling and validation in pure JavaScript

My intention is to get your thoughts and criticism about the script below, as regards the algorithm's design, performance and cross-browser compatibility.
I have just started getting into JavaScript having missed out on its awesomeness for quite a while. My background and experience is in developing C/C++/PHP based RESTful backends.
In order to understand the language and the right way of using it, I decided to do something which I am sure has been done many times before. But learning to use a new language and paradigm often entails pain anyway.
This is my attempt to create a normal form processing and validation script/ function.
In order to reduce complexity and keep code simple/clean, I decided to use HTML5 Custom Data Attributes (data-*) to assign metadata for each element in the form:
Data-Required: True or False. If set to true, this parameter makes the form-field required and so it cannot be empty. A value set to false indicates that the field is optional. Default is false.>
Data-Type: Type of validation to be performed. Examples include 'email', 'password', 'numbers' or any other 'regexp'.
A fairy simple example of such a form would be:
<form action="postlistings" id="postlistings" enctype='multipart/form-data' method="post" class="postlistings">
<ul class="login-li">
<li>
<input class="title" name="title" type="title" id="title" data-required="true" data-type="title"></a>
</li>
<li>
<textarea name="body" id="elm1" class="elm1" name="elm1" data-type="body" data-required="true" >
</textarea>
</li>
<li>
<span class="nav-btn-question">Add Listing</span>
</li>
</ul>
</form>
Reminder: This is my first piece of JavaScript code.
The idea is to call Form while passing the form name to retrieve and validate all the field values in one loop for performance. The validation involves two steps as can be guessed from the Data-* attributes described above:
i. Check for required form fields.
In case the values fail to meet step 1 requirement, an error message from configuration is pulled for the specific form value. Thus, for all values that fail to meet this requirement, an array of error messages are collected and passed on to the View.
ii. Perform respective validations.
Validations are only performed if all the values passed step 1. Otherwise, they follow the same steps as indicated in 1 above.
function Form(){
var args = Array.prototype.slice.call(arguments),
formName = args[0],
callback = args.pop(),
userError = [{type: {}, param: {}}],
requiredDataParam = 'required',
typeDataParam = 'type',
form = document.forms[formName],
formLength = form.length || null,
formElement = {id: {}, name: {}, value: {}, required: {}, type: {}};
function getFormElements(){
var num = 0;
var emptyContent = false;
for (var i = 0; i < formLength; i += 1) {
var formField = form[i];
formElement.id[i] = inArray('id', formField) ? formField.id : null;
formElement.name[i] = inArray('name', formField) ? formField.name : null;
formElement.value[i] = inArray('value', formField) ? formField.value : null;
formElement.required[i] = getDataAttribute(formField, requiredDataParam);
formElement.type[i] = getDataAttribute(formField, typeDataParam);
if (formElement.required[i] === true){
if(!formElement.type[i]) {
error('Validation rule not defined!');
}
else if (!formElement.value[i]) {
userError[num++] = {'type': 'required', 'param': form[i]};
emptyContent = true;
}
}
if (emptyContent === false) {
// Perform validations only if no empty but required form values were found.
// This is so that we can collect all the empty
// inputs and their corresponding error messages.
}
}
if (userError) {
// Return empty form errors and their corresponding error messages.
}
return formElement;
};
// Removed the getFormParam function that was not used at all.
return {
getFormElements: getFormElements
}
};
Two outside functions that are used in the JS script above (from JQuery source):
var inArray = function(elem, array){
if (array.indexOf){
return array.indexOf(elem);
}
for (var i = 0, length = array.length; i < length; i++){
if (array[i] === elem){
return i;
}
}
return -1;
}
// This is a cross-platform way to retrieve HTML5 custom attributes.
// Source: JQuery
var getDataAttribute = function(elem, key, data) {
if (data === undefined && elem.nodeType === 1) {
data = elem.getAttribute("data-" + key);
if (typeof data === "string") {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
!CheckType.isNaN ? parseFloat(data) :
CheckType.rbrace.test(data) ? parseJSON(data) :
data;
}
else {
data = undefined;
}
}
return data;
}
An example of Config Error messages can be set as follows:
var errorMsgs = {
ERROR_email: "Please enter a valid email address.",
ERROR_password: "Your password must be at least 6 characters long. Please try another",
ERROR_user_exists: "The requested email address already exists. Please try again."
};
As I post this for your review, please ignore any styling conventions that I might not have followed. My intention is to get your expert reviews on anything I should be doing different or could do better concerning the code itself, and the algorithm.
Besides the styling conventions, all criticism and questions are welcome.
First I'd like to clear up a common misconception. Forgive me if you already understand this clearly; maybe it will be helpful for someone else.
Learning and using jQuery or a similar library does not preclude or conflict with learning the JavaScript language. jQuery is simply a DOM manipulation library which takes away many of the pain points of using the DOM. There's plenty of room to learn and use JavaScript, the language, even if you use a library to abstract away some of the DOM details.
In fact, I would argue that using the DOM directly is likely to teach bad JavaScript coding habits, because the DOM is very much not a "JavaScript-ish" API. It was designed to work identically in JavaScript and Java and potentially other languages, and so it completely fails to make good use of the features of the JavaScript language.
Of course as you said, you're using this as a learning exercise; I just don't want you to fall into the trap that I've seen many people fall into of thinking, "I don't want to learn jQuery, because I want to learn JavaScript instead!" That's a false dichotomy: you have to learn JavaScript in either case, and using jQuery for the DOM doesn't interfere with that at all.
Now some details...
While it's OK to quote property names in an object literal and when you reference the properties, it's customary - and more readable - not to quote them when they are valid JavaScript names. e.g. in your formElement object
formElement = { id: {}, name: {}, value: {}, required: {}, type: {} };
(there was a missing semicolon at the end there too)
and where you use the names you can do:
formElement.id[i] = ...
formElement.name[i] = ...
etc.
Don't run your loops backwards unless the program logic requires it. It doesn't make the code faster except possibly in the case of an extremely tight loop, and it makes it unclear whether you're just prematurely optimizing or actually need the backwards loop.
Speaking of optimization, that loop has several inArray() calls. Since each of those loops through an array, that could be more of a performance impact than the outer loop. I imagine these arrays are probably pretty short? So performance wouldn't matter at all anyway, but this is something to think about in cases where you have longer arrays and objects. In some cases you can use an object with property names and values for a faster lookup - but I didn't look closely enough at what you're doing to suggest anything.
In any case, you're using inArray() wrong! But not your fault, that is a ridiculously named function in jQuery. The name clearly suggests a boolean return value, but the function returns the zero-based array index or -1 if the value is not found. I strongly recommend renaming this function as indexOf() to match the native Array method, or arrayIndex(), or some such.
That same loop has form[i] repeated numerous times. You could do this at the top of the loop:
var field = form[i];
and then use field throughout, e.g. field.id instead of form[i].id. This is generally faster, if it matters (which it probably doesn't here), but more importantly it's easier to read.
Do not use strict boolean comparisons like if( foo === true ) and if( bar === false) unless you really need to - and those cases are rare. The code sends a signal to the reader that there is something going on that's different from the usual boolean test. The only time these particular tests should be used is when you have a variable that may contain a boolean value or may contain some other type of value, and you need to distinguish which is which.
A good example of a case where you should use tests like these is an optional parameter that defaults to true:
// Do stuff unless 'really' is explicitly set to false, e.g.
// stuff(1) will do stuff with 1, but stuff(1,false) won't.
function stuff( value, really ) {
if( really === false ) {
// don't do stuff
}
else {
// do stuff
}
}
That specific example doesn't make a lot of sense, but it should give you the idea.
Similarly, an === true test could be used in a case where need to distinguish an actual boolean true value from some other "truthy" value. Indeed, it looks like this line is a valid case for that:
if (formElement['required'][i] === true){
given that if (formElement['required'][i] comes from the getDataAttribute() function which may return a boolean or other type.
If you are just testing for truthiness, though - and this should be most of the time - simply use if( foo ) or if( ! foo ). Or similarly in a conditional expression: foo ? x : y or !foo ? x : y.
The above was a long-winded way of saying that you should change this:
if (empty_content === false) {
to:
if (!empty_content) {
Your getFormParam() function goes to some work to convert an undefined result to null. There is usually no reason to do this. I don't see any place where that function is called, so I can't advise specifically, but in general you'd be testing for truthiness on something like this, so null and undefined would both be treated as false. Or in cases where you do need to distinguish null/undefined from other values (say, an explicit false), you can easily do it with != null or == null. This is one case where the "looser" comparison performed by == and != is very useful: both null and undefined evaluate the same with these operators.
You asked to ignore coding style, but one little suggestion here: You have a mix of camelCaseNames and names_with_underscores. In JavaScript, camelCaseNames are more idiomatic for function and variable names, with PascalCaseNames for constructor functions. Of course feel free to use underscores where they make more sense, for example if you're writing code that works with database columns in that format you may want your variable names to match the column names.
Hope that helps! Keep up the good work.
Update for your new code
I'm having a bit of trouble following the logic in the code, and I think I know part of the reason. It's a combination of naming conventions and inside-out objects.
First, the name formElement is really confusing. When I see element in JavaScript, I think of either a DOM element (HTMLElement) or an array element. I'm not sure if this formElement represents one or the other or neither.
So I look at the code to figure out what it's doing, and I see it has id:{}, name:{}, ... properties, but the code later treats each of those as an Array and not an Object:
formElement.id[i] = ...
formElement.name[i] = ...
formElement.value[i] = ...
formElement.required[i] = ...
formElement.type[i] = ...
(where i is an integer index)
If that code is right, those should be arrays instead: id:[], name:[], ....
But this is a red flag. When you see yourself creating parallel arrays in JavaScript, you're probably doing it wrong. In most cases you're better off replacing the parallel arrays with a single array of objects. Each of the objects in that array represents a single slice through all your parallel arrays, with a property for each of the previous arrays.
So, this object (where I've made the correction from {} to [] to match its current use):
formElement = { id: [], name: [], value: [], required: [], type: [] };
should be:
formInfo = [];
and then where you have the code that goes:
formElement.id[i] = ...;
formElement.name[i] = ...;
formElement.value[i] = ...;
formElement.required[i] = ...;
formElement.type[i] = ...;
It should be:
var info = {
id: ...,
name: ...,
value: ...,
required: ...,
type: ...
};
formInfo.push( info );
and adjust the rest of the code to suit. For example:
formElement.required[i]
would be:
formInfo[i].required
or even simpler since it's in the same function:
info.required
And note: I'm not saying info and formInfo are great names :-) they are just placeholders so you can think of a better name. The main idea is to create an array of objects instead of a set of parallel arrays.
One last thing and then I'm out of time for now.
That getDataAttribute() function is a complicated little piece of work. You don't need it! It would be simpler would just call the underlying function directly where you need it:
var info = {
...
required: formField.getAttribute('data-required') === 'true',
type: formField.getAttribute('data-type')
};
This also gives you full control of how the attributes are interpreted - as in the === 'true' test above. (This gives you a proper boolean value, so when you test the value later you don't have to use === true on it.)
On a stylistic note, yes, I did hard code the two 'data-xxxx' names right there, and I think that's a better and more clear way to do it.. Don't let your C experience throw you off here. There's no advantage to defining a string "constant" in this particular case, unless it's something that you want to make configurable, which this isn't.
Also, even if you do make a string constant, there's a minor advantage to having the complete 'data-whatever' string instead of just 'whatever'. The reason is that when somebody reads your HTML code, they may see a string in it and search the JS code for that string. But when they search for data-whatever they won't find it if the data- prefix is automagically prepended in the JS code.
Oh, I forgot one last thing. This code:
function Form(){
var args = Array.prototype.slice.call(arguments),
formName = args[0],
callback = args.pop(),
is working way too hard! Just do this instead:
function Form( formName, callback ) {
(and keep the var for the remaining variable declarations of course)
I cannot add comments yet so here is a little tip. I would separate the getFormElements() into smaller private functions. And I would add the errorMsgs to the Form function.
But for a first script in JavaScript, it is very impressive. This is actually the real reason I respond. I think it deserves more upvotes, and I would be very interested in a JS ninja responding to this question.
Good luck!

using try catch when declaring variables

For one reason or other ( I'm not going to go into why here) I need to use javascript to get the values of 12 hidden input fields and set a variable for each value.
I am not sure what the best approach for this would be. I'd like to be able to get the values and if they are not created i.e. the input fields are not there then id like to generate an error.
Would using a try / catch be good for this or should I simply be using typeof to check the variables have been created?
would putting them in an array as well so i can loop through to check their existance be a good idea?
thanks
This is the easy way of doing it. try-catch is rather heavy. Also, where would you throw the error to? Instead of unwinding your flow on error, collect your errors into a well structured response. That way if your first one is missing, but the other X are not, then you still get some work done.
if ( typeof( something ) !== "undefined" ) { doStuff(); }
Otherwise, I'd need more information to help you with your question.
Here's simple function that will check to see that exactly 12 input elements are included on the page. Please provide more information if you need th check that individual input elements are present.
function SaveInputValues() {
var inps = document.getElementsByTagName('input');
if (inps.length !== 12) {
return alert("There must be exactly 12 input elements. You have included " + inps.length + ".");
}
var vals = [];
for (i = 0; i < inps.length; i++) vals.push(inps[i].value);
inps = null; // provides closure
}
if(document.getElementById("someID")){
// add the value since the input exists
}
else{
// skip or use default - and inform if need be
}
Example implementation:
http://jsfiddle.net/zVz6h/1/
Code:
function getValueArray(idArray){
var valArray = [];
for(var i=0;i<idArray.length;i++){
if(document.getElementById(idArray[i])){
valArray.push(document.getElementById(idArray[i]).value);
}
else{
valArray.push("not defined");
}
}
return valArray;
}
var txtIDs = ["txt1","txt2","txt3","txt4","txt5","txt6","txt7","txt8"];
alert(getValueArray(txtIDs));

Categories

Resources