Programming optional ignorance - javascript

In Javascript what is the best way to handle scenarios when you have a set of arrays to perform tasks on sets of data and sometimes you do not want to include all of the arrays but instead a combination.
My arrays are labeled in this small snippet L,C,H,V,B,A,S and to put things into perspective the code is around 2500 lines like this. (I have removed code notes from this post)
if(C[0].length>0){
L=L[1].concat(+(MIN.apply(this,L[0])).toFixed(7));
C=C[1].concat(C[0][0]);
H=H[1].concat(+(MAX.apply(this,H[0])).toFixed(7));
V=V[1].concat((V[0].reduce(function(a,b){return a+b}))/(V[0].length));
B=B[1].concat((MAX.apply(this,B[0])-MIN.apply(this,B[0]))/2);
A=A[1].concat((MAX.apply(this,A[0])-MIN.apply(this,A[0]))/2);
D=D[1].concat((D[0].reduce(function(a,b){return a+b}))/(D[0].length));
S=S[1].concat((S[0].reduce(function(a,b){return a+b}))/(S[0].length));
}
It would seem counter-productive in this case to litter the code with tones of bool conditions asking on each loop or code section if an array was included in the task and even more silly to ask inside each loop iteration with say an inline condition as these would also slow down the processing and also make the code look like a maze or rabbit hole.
Is there a logical method / library to ignore instruction or skip if an option was set to false
All I have come up with so far is kind of pointless inline thing
var op=[0,1,1,0,0,0,0,0]; //options
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
op[0]&&[L[0]=1];
op[1]&&[C[0]=1,console.log('test, do more than one thing')];
op[2]&&[H[0]=1];
op[3]&&[V[0]=1];
op[4]&&[B[0]=1];
op[5]&&[A[0]=1];
op[6]&&[A[0]=1];
It works in that it sets only C[0] and H[0] to 1 as the options require, but it fails as it needs to ask seven questions per iteration of a loop as it may be done inside a loop. Rather than make seven versions of the the loop or code section, and rather than asking questions inside each loop is there another style / method?
I have also noticed that if I create an array then at some point make it equal to NaN rather than undefined or null the console does not complain
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
L=NaN;
L[0]=1;
//1
console.log(L); //NaN
L=undefined;
L[0]=1
//TypeError: Cannot set property '0' of undefined
L=null
L[0]=1
//TypeError: Cannot set property '0' of null
Am I getting warmer? I would assume that if I performed some math on L[0] when isNaN(L)===true that the math is being done but not stored so the line isn't being ignored really..

If I understand what you want I would do something like this.
var op = [...],
opchoice = {
//these can return nothing, no operation, or a new value.
'true': function(val){ /*operation do if true*/ },
'false': function(val){ /*operation do if false*/ },
//add more operations here.
//keys must be strings, or transformed into strings with operation method.
operation: function(val){
//make the boolean a string key.
return this[''+(val == 'something')](val);
}
};
var endop = [];//need this to prevent infinite recursion(loop).
var val;
while(val = op.shift()){
//a queue operation.
endop.push(opchoice.operation(val));
}
I'm sure this is not exactly what you want, but it's close to fulfilling the want of not having a ton of conditions every where.
Your other option is on every line do this.
A = isNaN(A) ? A.concat(...) : A;
Personally I prefer the other method.

It looks like you repeat many of the operations. These operations should be functions so at least you do not redefine the same function over and over again (it is also an optimization to do so).
function get_min(x)
{
return +(MIN.apply(this, a[0])).toFixed(7);
}
function get_max(x)
{
return +(MAX.apply(this, a[0])).toFixed(7);
}
function get_average(x)
{
return (x[0].reduce(function(a, b) {return a + b})) / (x[0].length);
}
function get_mean(x)
{
return (MAX.apply(this, x[0]) - MIN.apply(this, x[0])) / 2;
}
if(C[0].length > 0)
{
L = L[1].concat(get_min(L));
C = C[1].concat(C[0][0]);
H = H[1].concat(get_max(H));
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A);
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
You could also define an object with prototype functions, but it is not clear whether it would be useful (outside of putting those functions in a namespace).
In regard to the idea/concept of having a test, what you've found is probably the best way in JavaScript.
op[0] && S = S[1].concat(get_average(S));
And if you want to apply multiple operators when op[0] is true, use parenthesis and commas:
op[3] && (V = V[1].concat(get_average(V)),
B = B[1].concat(get_mean(B)),
A = A[1].concat(get_mean(A));
op[0] && (D = D[1].concat(get_average(D)),
S = S[1].concat(get_average(S)));
However, this is not any clearer, to a programmer, than an if() block as shown in your question. (Actually, many programmers may have to read it 2 or 3 times before getting it.)
Yet, there is another solution which is to use another function layer. In that last example, you would do something like this:
function VBA()
{
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A));
}
function DS()
{
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
op = [DS,null,null,VBA,null,null,...];
for(key in op)
{
// optional: if(op[key].hasOwnProperty(key)) ... -- verify that we defined that key
if(op[key])
{
op[key](); // call function
}
}
So in other words you have an array of functions and can use a for() loop to go through the various items and if defined, call the function.
All of that will very much depend on the number of combinations you have. You mentioned 2,500 lines of code, but the number of permutations may be such that writing it one way or the other will possibly not reduce the total number of lines, but it will make it easier to maintain because many lines are moved to much smaller code snippet making the overall program easier to understand.
P.S. To make it easier to read and debug later, I strongly suggest you put more spaces everywhere, as shown above. If you want to save space, use a compressor (minimizer), Google or Yahoo! both have one that do a really good job. No need to write your code pre-compressed.

Related

How to return multiple tokens with Jison lexer

I'm new to lexing and parsing so sorry if the title isn't clear enough.
Basically, I'm using Jison to parse some text and I am trying to get the lexer to comprehend indentation. Here's the bit in question:
(\r\n|\r|\n)+\s* %{
parser.indentCount = parser.indentCount || [0];
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var tokens = [];
while (indentation < parser.indentCount[0]) {
tokens.push('DEDENT');
parser.indentCount.shift();
}
if (tokens.length) {
return tokens;
}
if (!indentation.length) {
return 'NEWLINE';
}
%}
So far, almost all of that works as expected. The one problem is the line where I attempt to return an array of DEDENT tokens. It appears that Jison is just converting that array into a string which causes me to get a parse error like Expecting ........, got DEDENT,DEDENT.
What I'm hoping I can do to get around this is manually push some DEDENT tokens onto the stack. Maybe with a function like this.pushToken('DEDENT') or something along those lines. But the Jison documentation is not so great and I could use some help.
Any thoughts?
EDIT:
I seem to have been able to hack my way around this after looking at the generated parser code. Here's what seems to work...
if (tokens.length) {
var args = arguments;
tokens.slice(1).forEach(function () {
lexer.performAction.apply(this, args);
}.bind(this));
return 'DEDENT';
}
This tricks the lexer into performing another action using the exact same input for each DEDENT we have in the stack, thus allowing it to add in the proper dedents. However, it feels gross and I'm worried there could be unforeseen problems.
I would still love it if anyone had any ideas on a better way to do this.
After a couple of days I ended up figuring out a better answer. Here's what it looks like:
(\r\n|\r|\n)+[ \t]* %{
parser.indentCount = parser.indentCount || [0];
parser.forceDedent = parser.forceDedent || 0;
if (parser.forceDedent) {
parser.forceDedent -= 1;
this.unput(yytext);
return 'DEDENT';
}
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var dedents = [];
while (indentation < parser.indentCount[0]) {
dedents.push('DEDENT');
parser.indentCount.shift();
}
if (dedents.length) {
parser.forceDedent = dedents.length - 1;
this.unput(yytext);
return 'DEDENT';
}
return `NEWLINE`;
%}
Firstly, I modified my capture regex to make sure I wasn't inadvertently capturing extra newlines after a series of non-newline spaces.
Next, we make sure there are 2 "global" variables. indentCount will track our current indentation length. forceDedent will force us to return a DEDENT if it has a value above 0.
Next, we have a condition to test for a truthy value on forceDedent. If we have one, we'll decrement it by 1 and use the unput function to make sure we iterate on this same pattern at least one more time, but for this iteration, we'll return a DEDENT.
If we haven't returned, we get the length of our current indentation.
If the current indentation is greater than our most recent indentation, we'll track that on our indentCount variable and return an INDENT.
If we haven't returned, it's time to prepare to possible dedents. We'll make an array to track them.
When we detect a dedent, the user could be attempting to close 1 or more blocks all at once. So we need to include a DEDENT for as many blocks as the user is closing. We set up a loop and say that for as long as the current indentation is less than our most recent indentation, we'll add a DEDENT to our list and shift an item off of our indentCount.
If we tracked any dedents, we need to make sure all of them get returned by the lexer. Because the lexer can only return 1 token at a time, we'll return 1 here, but we'll also set our forceDedent variable to make sure we return the rest of them as well. To make sure we iterate on this pattern again and those dedents can be inserted, we'll use the unput function.
In any other case, we'll just return a NEWLINE.

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Get a return value from arbitrary eval'd code

I have a requirement that the user can provide arbitrary statements which can be stored in a function and called later to get a return value. A simple example of this is that userInput might be
var x = 10;
x;
I would store this via
var callback = function() {
return eval(userInput);
}
and then running callback() returns 10 as expected.
However, I also need to support the case with an explicit return statement, ie userInput might be
var x = 10;
return x;
In this case the eval method above will fail with SyntaxError: return not in function. Instead I could store callback as
var callback = new Function(userInput);
My issue is that I would like to combine these two approaches according the rule 'get explicit return value otherwise get the result of the last executed statement'. In particular this can't be done with analysis of the code at callback creation time as the user could potentially do something odd like
if(envVar < 10)
return a;
b * 0.5;
which combines both.
Any thoughts on how to structure the creation of the callback function to accommodate these possible inputs? Unfortunately it is not possible to enforce one style or the other on the user.
UPDATE to answer some of the comments below.
One solution suggested is to search for a return token and choose between new Function and eval. This doesn't work for my last example, see http://jsfiddle.net/ZGb6z/2/ - out4 should be "no" but ends up being undefined as the last executed statement is not returned.
Another suggestion is to modify the user input to add an explicit return on the last line if one is not found. Unfortunately it's not possible to know which statement will be executed last. Consider
var x = 10;
switch(x) {
case 10:
100;
break;
default:
200;
break;
}
When called it should return 100, but it would take some serious analysis to determine where to put returns for arbitrary code.
Just use a try catch, manipulating the input will be very painful for you, and try catch can't really make your code any more obtuse at this point.
var failback = function () {
try {
return eval(userInput);
} catch (e) {
return Function(userInput);
}
};
What I would really suggest is investing in a parser, kind of like how Angular does it. That kind of thing would prevent your users from doing whatever the hell they want, introducing attack vectors, yadda, yadda, yadda.
Either manage your expectations or manage your user's expectations. eval and new Function() are not suitable for your requirements if you require mixed usage of explicit and non-explicit return statements in the same user-input. You will continue to find issues following either of these routes.
Simply searching for the word return is not sufficient either... var returning = true; or var a = 'return'; or /* return true */ true; will all throw false positives.
Managing your expectations: To do such a thing you require a form of lexer and parser, at which point you can do away with eval entirely and execute your own safe functions based on the parsed input. This is the best approach when execution of user input has to occur anyway as you can ensure that nothing gets executed you do not wish to permit. If you want to cover these sort of edge cases and permit strange user input then you must be prepared to increase the size and development time of your application. I have built a few applications executing user generated code and have always come to the conclusion this is the correct route to go down.
Managing your user's expectations: Provide a guide, tell them not to mix explicit returns with non-explicit returns, these are strange coding practices anyway. Better yet explicitly tell them to include or omit the return statement. There is no shame in asking your users to follow them, especially if it allows you to improve their experience elsewhere.
There I was thinking I'd only see problems like this at the code golf stack exchange :)
My solution is here: http://jsfiddle.net/hKq87/1
It essentially replaces the 'return' statement with an exception that has a special string prefixed to it. If we see that string, we know we are actually returning a value, and return it rather than re-raising the exception.
The reason I chose to throw an exception rather than replace the return statement with a function call was because it is hard to know where the JS code evaluated for the return really ends. It could be split across multiple lines, contain several special characters and may not even have the optional semi-colon at the end. So I concatenate a string to whatever the value being returned is and throw it, as the throw keyword doesn't require it's argument to be wrapped in parentheses.
In addition, throwing exceptions provides me a convenient way to immediately terminate execution of the code block, without halting other JS execution.
Here is the callback method:
var callback = function(userInput) {
var returned = undefined;
userInput = userInput.replace(/(^|[\(\\)[\]{};,\s])return(\s*[^\s;])?/gi, function(m, b, e){
return b + " throw '__RETURNED_VALUE'" +
(e !== undefined ? "+" + e : "");
});
try {
returned = eval(userInput);
} catch (e) {
if (e.indexOf("__RETURNED_VALUE") == 0) {
returned = e.substring("__RETURNED_VALUE".length) || undefined;
}
else {
throw e;
}
}
return returned;
}
The regex above accounts for variables that may end with the string "return", that we would not want to replace as it is not a return statement. It also allows for return statements within braces, without trailing semi-colons or at the very beginning/end.
One issue with the current method is that you can not use the (rare) comma operator in a return statement, or expect numerical values to be returned correctly. The last test case in the jsfiddle demonstrates this. An example from here:
//original
return 5 * 2 + 3, 22;
//modified
throw '__RETURNED_VALUE='+ 5 * 2 + 3, 22;
//apply * operator
throw '__RETURNED_VALUE='+ 10 + 3, 22;
//apply + operators
throw '__RETURNED_VALUE=103', 22;
//apply , operator
throw 22;
This problem can be avoided by completely eliminating the prefix '__RETURNED_VALUE=' and just replacing 'return' with 'throw'. However, this means that the code provided must run without throwing exceptions, which I thought to be a harder constraint than just crafting return statements to be simple (avoiding comma operators, non-parenthesized arithmetic, etc.). In addition, if a user ever creates a return statement that we can't handle with the current code, we conveniently throw an exception for it so it easily comes to our attention.
jsFiddle Demo
Lets assume your user can be a little smarter than the average bear. We are going to ask them to specifically provide an initial value and then a callback with an expression for that value.
The main benefit of doing this is that you avoid eval, and actually have a nice implementation that is re-usable as opposed to being subject to later refactoring.
This way also provides a separation of where the input comes from and where the examination comes from. Although the provided example only shows integer input, really it could be another call with absolutely no knowledge of the value aside that it needs to conform to the callback logic.
function expression(x,callback){
return callback(x);
}
out1.innerHTML = expression(8,function(x){
return x;
});
out2.innerHTML = expression(10,function(x){
return x;
});
out3.innerHTML = expression(10,function(x){
if(x === 10) return "yes"; "no";
});
out4.innerHTML = expression(8,function(x){
return x === 10 ? "yes" : "no";
});

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!

JavaScript Infinitely Looping slideshow with delays?

How do I make an infinite loop in JavaScript? I'm trying to make a slideshow, which I have working, but I can't get it to loop. I can't even get it to loop twice.
The code I'm using right now is
window.onload = function start() {
slide();
}
function slide() {
var num = 0;
for (num=0;num<=10;num++) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
Without the for thing in there, it does go through once. When I put in a for, it either makes Firefox lock up, or just loops once. I'm sure this is a really simple thing to do, and even if it has to be loop 1,000,000 times or something instead of infinite, that'd work fine for me.
Also, I don't want to use jQuery or something that someone else created. I'm learning JavaScript, and this is partially to help me learn, and partially because I'm trying to make as many HTML5-based systems as I can.
EDIT: I think the reason it's freezing is because it executes the code all at once, and then just stores it in a cache or something. What I want it to do is go through this once, then start at the top again, which is what I've always thought loops where for. In "batch" (command prompt) scripting, this could be done with a "GOTO" command. I don't know if there's an equivalent in JS or not, but that's really my goal.
The correct approach is to use a single timer. Using setInterval, you can achieve what you want as follows:
window.onload = function start() {
slide();
}
function slide() {
var num = 0, style = document.getElementById('container').style;
window.setInterval(function () {
// increase by num 1, reset to 0 at 4
num = (num + 1) % 4;
// -600 * 1 = -600, -600 * 2 = -1200, etc
style.marginLeft = (-600 * num) + "px";
}, 3000); // repeat forever, polling every 3 seconds
}
You don't want while(true), that will lock up your system.
What you want instead is a timeout that sets a timeout on itself, something like this:
function start() {
// your code here
setTimeout(start, 3000);
}
// boot up the first call
start();
Here's a nice, tidy solution for you: (also see the live demo ->)
window.onload = function start() {
slide();
}
function slide() {
var currMarg = 0,
contStyle = document.getElementById('container').style;
setInterval(function() {
currMarg = currMarg == 1800 ? 0 : currMarg + 600;
contStyle.marginLeft = '-' + currMarg + 'px';
}, 3000);
}
Since you are trying to learn, allow me to explain how this works.
First we declare two variables: currMarg and contStyle. currMarg is an integer that we will use to track/update what left margin the container should have. We declare it outside the actual update function (in a closure), so that it can be continuously updated/access without losing its value. contStyle is simply a convenience variable that gives us access to the containers styles without having to locate the element on each interval.
Next, we will use setInterval to establish a function which should be called every 3 seconds, until we tell it to stop (there's your infinite loop, without freezing the browser). It works exactly like setTimeout, except it happens infinitely until cancelled, instead of just happening once.
We pass an anonymous function to setInterval, which will do our work for us. The first line is:
currMarg = currMarg == 1800 ? 0 : currMarg + 600;
This is a ternary operator. It will assign currMarg the value of 0 if currMarg is equal to 1800, otherwise it will increment currMarg by 600.
With the second line, we simply assign our chosen value to containers marginLeft, and we're done!
Note: For the demo, I changed the negative values to positive, so the effect would be visible.
Perhps this is what you are looking for.
var pos = 0;
window.onload = function start() {
setTimeout(slide, 3000);
}
function slide() {
pos -= 600;
if (pos === -2400)
pos = 0;
document.getElementById('container').style.marginLeft= pos + "px";
setTimeout(slide, 3000);
}
You are calling setTimeout() ten times in a row, so they all expire almost at the same time. What you actually want is this:
window.onload = function start() {
slide(10);
}
function slide(repeats) {
if (repeats > 0) {
document.getElementById('container').style.marginLeft='-600px';
document.getElementById('container').style.marginLeft='-1200px';
document.getElementById('container').style.marginLeft='-1800px';
document.getElementById('container').style.marginLeft='0px';
window.setTimeout(
function(){
slide(repeats - 1)
},
3000
);
}
}
This will call slide(10), which will then set the 3-second timeout to call slide(9), which will set timeout to call slide(8), etc. When slide(0) is called, no more timeouts will be set up.
You can infinitely loop easily enough via recursion.
function it_keeps_going_and_going_and_going() {
it_keeps_going_and_going_and_going();
}
it_keeps_going_and_going_and_going()
The key is not to schedule all pics at once, but to schedule a next pic each time you have a pic shown.
var current = 0;
var num_slides = 10;
function slide() {
// here display the current slide, then:
current = (current + 1) % num_slides;
setTimeout(slide, 3000);
}
The alternative is to use setInterval, which sets the function to repeat regularly (as opposed to setTimeout, which schedules the next appearance only.
Expanding on Ender's answer, let's explore our options with the improvements from ES2015.
First off, the problem in the asker's code is the fact that setTimeout is asynchronous while loops are synchronous. So the logical flaw is that they wrote multiple calls to an asynchronous function from a synchronous loop, expecting them to execute synchronously.
function slide() {
var num = 0;
for (num=0;num<=10;num++) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
What happens in reality, though, is that...
The loop "simultaneously" creates 44 async timeouts set to execute 3, 6, 9 and 12 seconds in the future. Asker expected the 44 calls to execute one-after-the-other, but instead, they all execute simultaneously.
3 seconds after the loop finishes, container's marginLeft is set to "-600px" 11 times.
3 seconds after that, marginLeft is set to "-1200px" 11 times.
3 seconds later, "-1800px", 11 times.
And so on.
You could solve this by changing it to:
function setMargin(margin){
return function(){
document.querySelector("#container").style.marginLeft = margin;
};
}
function slide() {
for (let num = 0; num <= 10; ++num) {
setTimeout(setMargin("-600px"), + (3000 * (num + 1)));
setTimeout(setMargin("-1200px"), + (6000 * (num + 1)));
setTimeout(setMargin("-1800px"), + (9000 * (num + 1)));
setTimeout(setMargin("0px"), + (12000 * (num + 1)));
}
}
But that is just a lazy solution that doesn't address the other issues with this implementation. There's a lot of hardcoding and general sloppiness here that ought to be fixed.
Lessons learnt from a decade of experience
As mentioned at the top of this answer, Ender already proposed a solution, but I would like to add on to it, to factor in good practice and modern innovations in the ECMAScript specification.
function format(str, ...args){
return str.split(/(%)/).map(part => (part == "%") ? (args.shift()) : (part)).join("");
}
function slideLoop(margin, selector){
const multiplier = -600;
let contStyle = document.querySelector(selector).style;
return function(){
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
}
}
function slide() {
return setInterval(slideLoop(0, "#container"), 3000);
}
Let's go over how this works for the total beginners (note that not all of this is directly related to the question):
format
function format
It's immensely useful to have a printf-like string formatter function in any language. I don't understand why JavaScript doesn't seem to have one.
format(str, ...args)
... is a snazzy feature added in ES6 that lets you do lots of stuff. I believe it's called the spread operator. Syntax: ...identifier or ...array. In a function header, you can use it to specify variable arguments, and it will take every argument at and past the position of said variable argument, and stuff them into an array. You can also call a function with an array like so: args = [1, 2, 3]; i_take_3_args(...args), or you can take an array-like object and transform it into an array: ...document.querySelectorAll("div.someclass").forEach(...). This would not be possible without the spread operator, because querySelectorAll returns an "element list", which isn't a true array.
str.split(/(%)/)
I'm not good at explaining how regex works. JavaScript has two syntaxes for regex. There's the OO way (new RegExp("regex", "gi")) and there's the literal way (/insert regex here/gi). I have a profound hatred for regex because the terse syntax it encourages often does more harm than good (and also because they're extremely non-portable), but there are some instances where regex is helpful, like this one. Normally, if you called split with "%" or /%/, the resulting array would exclude the "%" delimiters from the array. But for the algorithm used here, we need them included. /(%)/ was the first thing I tried and it worked. Lucky guess, I suppose.
.map(...)
map is a functional idiom. You use map to apply a function to a list. Syntax: array.map(function). Function: must return a value and take 1-2 arguments. The first argument will be used to hold each value in the array, while the second will be used to hold the current index in the array. Example: [1,2,3,4,5].map(x => x * x); // returns [1,4,9,16,25]. See also: filter, find, reduce, forEach.
part => ...
This is an alternative form of function. Syntax: argument-list => return-value, e.g. (x, y) => (y * width + x), which is equivalent to function(x, y){return (y * width + x);}.
(part == "%") ? (args.shift()) : (part)
The ?: operator pair is a 3-operand operator called the ternary conditional operator. Syntax: condition ? if-true : if-false, although most people call it the "ternary" operator, since in every language it appears in, it's the only 3-operand operator, every other operator is binary (+, &&, |, =) or unary (++, ..., &, *). Fun fact: some languages (and vendor extensions of languages, like GNU C) implement a two-operand version of the ?: operator with syntax value ?: fallback, which is equivalent to value ? value : fallback, and will use fallback if value evaluates to false. They call it the Elvis Operator.
I should also mention the difference between an expression and an expression-statement, as I realize this may not be intuitive to all programmers. An expression represents a value, and can be assigned to an l-value. An expression can be stuffed inside parentheses and not be considered a syntax error. An expression can itself be an l-value, although most statements are r-values, as the only l-value expressions are those formed from an identifier or (e.g. in C) from a reference/pointer. Functions can return l-values, but don't count on it. Expressions can also be compounded from other, smaller expressions. (1, 2, 3) is an expression formed from three r-value expressions joined by two comma operators. The value of the expression is 3. expression-statements, on the other hand, are statements formed from a single expression. ++somevar is an expression, as it can be used as the r-value in the assignment expression-statement newvar = ++somevar; (the value of the expression newvar = ++somevar, for example, is the value that gets assigned to newvar). ++somevar; is also an expression-statement.
If ternary operators confuse you at all, apply what I just said to the ternary operator: expression ? expression : expression. Ternary operator can form an expression or an expression-statement, so both of these things:
smallest = (a < b) ? (a) : (b);
(valueA < valueB) ? (backup_database()) : (nuke_atlantic_ocean());
are valid uses of the operator. Please don't do the latter, though. That's what if is for. There are cases for this sort of thing in e.g. C preprocessor macros, but we're talking about JavaScript here.
args.shift()
Array.prototype.shift. It's the mirror version of pop, ostensibly inherited from shell languages where you can call shift to move onto the next argument. shift "pops" the first argument out of the array and returns it, mutating the array in the process. The inverse is unshift. Full list:
array.shift()
[1,2,3] -> [2,3], returns 1
array.unshift(new-element)
[element, ...] -> [new-element, element, ...]
array.pop()
[1,2,3] -> [1,2], returns 3
array.push(new-element)
[..., element] -> [..., element, new-element]
See also: slice, splice
.join("")
Array.prototype.join(string). This function turns an array into a string. Example: [1,2,3].join(", ") -> "1, 2, 3"
slide
return setInterval(slideLoop(0, "#container"), 3000);
First off, we return setInterval's return value so that it may be used later in a call to clearInterval. This is important, because JavaScript won't clean that up by itself. I strongly advise against using setTimeout to make a loop. That is not what setTimeout is designed for, and by doing that, you're reverting to GOTO. Read Dijkstra's 1968 paper, Go To Statement Considered Harmful, to understand why GOTO loops are bad practice.
Second off, you'll notice I did some things differently. The repeating interval is the obvious one. This will run forever until the interval is cleared, and at a delay of 3000ms. The value for the callback is the return value of another function, which I have fed the arguments 0 and "#container". This creates a closure, and you will understand how this works shortly.
slideLoop
function slideLoop(margin, selector)
We take margin (0) and selector ("#container") as arguments. The margin is the initial margin value and the selector is the CSS selector used to find the element we're modifying. Pretty straightforward.
const multiplier = -600;
let contStyle = document.querySelector(selector).style;
I've moved some of the hard coded elements up. Since the margins are in multiples of -600, we have a clearly labeled constant multiplier with that base value.
I've also created a reference to the element's style property via the CSS selector. Since style is an object, this is safe to do, as it will be treated as a reference rather than a copy (read up on Pass By Sharing to understand these semantics).
return function(){
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
}
Now that we have the scope defined, we return a function that uses said scope. This is called a closure. You should read up on those, too. Understanding JavaScript's admittedly bizarre scoping rules will make the language a lot less painful in the long run.
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
Here, we simply increment margin and modulus it by 4. The sequence of values this will produce is 1->2->3->0->1->..., which mimics exactly the behavior from the question without any complicated or hard-coded logic.
Afterwards, we use the format function defined earlier to painlessly set the marginLeft CSS property of the container. It's set to the currnent margin value multiplied by the multiplier, which as you recall was set to -600. -600 -> -1200 -> -1800 -> 0 -> -600 -> ...
There are some important differences between my version and Ender's, which I mentioned in a comment on their answer. I'm gonna go over the reasoning now:
Use document.querySelector(css_selector) instead of document.getElementById(id)
querySelector was added in ES6, if I'm not mistaken. querySelector (returns first found element) and querySelectorAll (returns a list of all found elements) are part of the prototype chain of all DOM elements (not just document), and take a CSS selector, so there are other ways to find an element than just by its ID. You can search by ID (#idname), class (.classname), relationships (div.container div div span, p:nth-child(even)), and attributes (div[name], a[href=https://google.com]), among other things.
Always track setInterval(fn, interval)'s return value so it can later be closed with clearInterval(interval_id)
It's not good design to leave an interval running forever. It's also not good design to write a function that calls itself via setTimeout. That is no different from a GOTO loop. The return value of setInterval should be stored and used to clear the interval when it's no longer needed. Think of it as a form of memory management.
Put the interval's callback into its own formal function for readability and maintainability
Constructs like this
setInterval(function(){
...
}, 1000);
Can get clunky pretty easily, especially if you are storing the return value of setInterval. I strongly recommend putting the function outside of the call and giving it a name so that it's clear and self-documenting. This also makes it possible to call a function that returns an anonymous function, in case you're doing stuff with closures (a special type of object that contains the local state surrounding a function).
Array.prototype.forEach is fine.
If state is kept with the callback, the callback should be returned from another function (e.g. slideLoop) to form a closure
You don't want to mush state and callbacks together the way Ender did. This is mess-prone and can become hard to maintain. The state should be in the same function that the anonymous function comes from, so as to clearly separate it from the rest of the world. A better name for slideLoop could be makeSlideLoop, just to make it extra clear.
Use proper whitespace. Logical blocks that do different things should be separated by one empty line
This:
print(some_string);
if(foo && bar)
baz();
while((some_number = some_fn()) !== SOME_SENTINEL && ++counter < limit)
;
quux();
is much easier to read than this:
print(some_string);
if(foo&&bar)baz();
while((some_number=some_fn())!==SOME_SENTINEL&&++counter<limit);
quux();
A lot of beginners do this. Including little 14-year-old me from 2009, and I didn't unlearn that bad habit until probably 2013. Stop trying to crush your code down so small.
Avoid "string" + value + "string" + .... Make a format function or use String.prototype.replace(string/regex, new_string)
Again, this is a matter of readability. This:
format("Hello %! You've visited % times today. Your score is %/% (%%).",
name, visits, score, maxScore, score/maxScore * 100, "%"
);
is much easier to read than this horrific monstrosity:
"Hello " + name + "! You've visited " + visits + "% times today. " +
"Your score is " + score + "/" + maxScore + " (" + (score/maxScore * 100) +
"%).",
edit: I'm pleased to point out that I made in error in the above snippet, which in my opinion is a great demonstration of how error-prone this method of string building is.
visits + "% times today"
^ whoops
It's a good demonstration because the entire reason I made that error, and didn't notice it for as long as I did(n't), is because the code is bloody hard to read.
Always surround the arguments of your ternary expressions with parens. It aids readability and prevents bugs.
I borrow this rule from the best practices surrounding C preprocessor macros. But I don't really need to explain this one; see for yourself:
let myValue = someValue < maxValue ? someValue * 2 : 0;
let myValue = (someValue < maxValue) ? (someValue * 2) : (0);
I don't care how well you think you understand your language's syntax, the latter will ALWAYS be easier to read than the former, and readability is the the only argument that is necessary. You read thousands of times more code than you write. Don't be a jerk to your future self long-term just so you can pat yourself on the back for being clever in the short term.
Here:
window.onload = function start() {
slide();
}
function slide() {
var num = 0;
for (num=0;num==10;) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
That makes it keep looping alright! That's why it isn't runnable here.
try this:
window.onload = function start() {
slide();
}
function slide() {
setInterval("document.getElementById('container').style.marginLeft='-600px'",3000);
setInterval("document.getElementById('container').style.marginLeft='-1200px'",6000);
setInterval("document.getElementById('container').style.marginLeft='-1800px'",9000);
setInterval("document.getElementById('container').style.marginLeft='0px'",12000);
}
setInterval is basically an 'infinite loop' and it wont black up the browser. it waits the required time, then goes again
you can use requestAnimationFrame() function like in the below,
function unlimited () {
requestAnimationFrame(unlimited);
console.log("arian")
}
unlimited();

Categories

Resources