about a replaceText function - javascript

In the following jQuery replaceText function, can someone please explain to me what the line remove.length && $(remove).remove(); does? I don't understand the usage of && here.
$.fn.replaceText = function( search, replace, text_only ) {
return this.each(function(){
var node = this.firstChild,
val,
new_val,
remove = [];
if ( node ) {
do {
if ( node.nodeType === 3 ) {
val = node.nodeValue;
new_val = val.replace( search, replace );
if ( new_val !== val ) {
if ( !text_only && /</.test( new_val ) ) {
$(node).before( new_val );
remove.push( node );
} else {
node.nodeValue = new_val;
}
}
}
} while ( node = node.nextSibling );
}
remove.length && $(remove).remove();
});
};

Due to the way conditions short circuit, $(remove).remove(); will only be evaluated if remove.length is true-ish.
Since remove is declared as an array, it's basically the same as:
if(remove.length != 0)
$(remove).remove();

The && here means that $(remove).remove() will only be executed if remove.length isn't 0.
This is because the && is a short circuit operator so it will not evaluate whatever comes after it if what is before it evaluates to false.

Though you got some right answers about what it does:
if(remove.length) // length is bigger than 0 (in this context)
$(remove).remove();
I must say that code is stupid, jQuery knows how to handle empty sets.
So when $(remove).length == 0 the code $(remove).remove(); just won't do anything.
You can simply use it without the if:
$(remove).remove();

it says :
if remove.length returns something other than 0 (which is equivalent to false) then remove the element

It is making sure that the "remove" object is set
if(remove.length != 0)
so that it does not try to remove something that does not exist. In jQuery, if the object $(remove) does not exist, the .length function will return 0 which coincides with False in javascript.
If it does exist (remove.length > 0) then it will be removed using the .remove() function. The naming is ambiguous for sure.
It's basically an in line condition statement to prevent an exception by calling .remove() on an object that doesn't exist.

Related

Javascript string in array or a part

I have a Javascript array with multiple values:
var filterClasses = ['col-sm-12', 'hidden-xs', 'hidden-sm', 'hidden-lg', 'hidden-md', 'active', 'btn-'];
I have a function that gets all css classes in the DOM. But i want to check if this class should be added to a new array or not. So i can use indexOf for this:
return filterClasses.indexOf('col-sm-12');
This returns a true, so this class should be ignored.
But now i have a class that is btn-primary. As you see in my array i have the btn- added in it. I want to exclude all classes that contains the word btn-. How can i achieve this?
Current function:
function setupShouldAddClass( cssClass, filterClasses )
{
// If the cssClass exists in the filterClasses then false
if ( filterClasses.indexOf(cssClass) > 0 )
{
return true;
}
filterClasses.forEach(function ( item )
{
if ( stringContains(item, cssClass) )
{
return true;
}
});
return false;
}
function stringContains( needle, haystack )
{
return (haystack.indexOf(needle) !== -1);
}
Maybe you can solve your issue using regular expressions instead of using imperative code:
var classBlackListRegExp = /(col-sm-12|hidden-xs|hidden-sm|hidden-lg|hidden-md|active|^btn-.+)/i;
var result = classBlackListRegExp.test("btn-whatever");
console.log(result);
Check the ^btn-.+ part. This matches anything starting with "btn-".
I believe that your scenario is the ideal use case of regular expressions!
OP concerns if class black list is very large
OP said:
what im wondering is, that if i add more then 100 classes, how does
this handle the line breaks?
You can join the whole array of black-listed strings and create a RegExp object with it as follows:
// I use a templated string and String.prototype.join
// to create a regular expression from a given array:
var classBlackListRegExp = new RegExp(`(${[
'col-sm-12',
'hidden-xs',
'hidden-sm',
'hidden-lg',
'hidden-md',
'active',
'^btn-.+'
].join("|")})`, "i");
var result = classBlackListRegExp.test("btn-whatever");
console.log(result);
You need to use Array#some and check against each value and return true if found.
function setupShouldAddClass(cssClass, filterClasses) {
return filterClasses.indexOf(cssClass) !== -1 || filterClasses.some(function (item) {
return stringContains(item, cssClass);
});
}
I would loop over them like this.
function setupShouldAddClass( cssClass, filterClasses )
{
// If the cssClass exists in the filterClasses then false
var ret = true;
filterClasses.forEach(function(el) {
if (cssClass.indexOf(el) >= 0) {
ret = false;
}
});
return ret;
}
How about bringing the cssClass to what you needed to be compared to:
var transformedToMyNeedsCSS = "btn-primary".replace(/^(btn[-])(?:.*)/,"$1");
// --> Output "bnt-"
And then you compare as you are doing it now:
if ( filterClasses.indexOf(transformedToMyNeedsCSS) > 0 )
{
return true;
}
There's a whole host going wrong here. Let's deal with it step by step:
First
if ( filterClasses.indexOf(cssClass) > 0 )
That is incorrect because indexOf returns -1 if the search term is not found. It returns 0 if it is the first item in the array. So you want >= 0.
Second, the forEach loop:
filterClasses.forEach(function ( item )
{
if ( stringContains(item, cssClass) )
{
return true;
}
});
This achieves nothing. As in, genuinely nothing. Because you are inside a new function (the callback to forEach) return only returns from the inner function. And the return value is then discarded. What this code actually does is loop all the way through the array and do nothing. What you actually want is a clever function called Array.prototype.some. This loops through the array and tests each element. If you return true on any of the elements, some returns true. Otherwise it returns false.
So your code could look like this:
return filterClasses.some(function(element) {
return stringContains(item, cssClass);
}
Now we want to ignore all classes where they begin with btn-. I presume this means that you want to return false if the class begins with btn-.
if (cssClass.indexOf('btn-') === 0) {
return false;
}
So your function now looks like:
function setupShouldAddClass( cssClass, filterClasses )
{
if (cssClass.indexOf('btn-') === 0) {
return false;
}
// If the cssClass exists in the filterClasses then false
if ( filterClasses.indexOf(cssClass) >= 0 )
{
return true;
}
return filterClasses.some(function(element) {
return stringContains(item, cssClass);
});
}

Why a null result is freezing the script and how to avoid it?

When I use the below code:
if(string.match(/td>0/g).length == 8) {
/*Do something*/
}
and no /td>0/ are matched, it returns a null result that prevents the script below from executing.
I would like to know why the code is freezing, and how to avoid it and find a solution or an alternative to .match() ?
You can simply add a null check first -
if(string.match(/td>0/g) != null && string.match(/td>0/g).length == 8) {
/*Do something*/
}
Try this(More Recommended):
var matching=string.match(/td>0/g);
if( matching != null && matching.length === 8) {
/*Do something*/
}
Use === instead of ==.
You are going to need to make a null check before checking the length. I would do either do the match first and than check
var result = string.match(/td>0/g);
if (result && result.length) {}
or use an or to catch the null
if( (string.match(/td>0/g)||"").length ) {}

Check exactly one boolean option set

Well, this is kind of hacky:
function b2n(boo) {
return boo ? 1 : 0;
}
if(b2n(opt1) + b2n(opt2) + b2n(opt3) !== 1) {
throw new Error("Exactly one option must be set");
}
Is there a better way to do this in Javascript? Using any of
more intelligent boolean/number handling
sneaky array or functional operations
And so forth. Javascript and Node solutions welcome.
In my actual problem, the options are coming from the Node module commander, so I'm not dealing with true boolean, just truthy and falsy things. There may be a commander-solution too.
Assuming you had an array of options, you could do:
if(opts.filter(Boolean).length !== 1) {}
It seems to me though that you ought to have one variable with three possible states instead...
var opt = 'a'; // (or 'b', or 'c')
You can do this :
if ( !!opt1 + !!opt2 + !!opt3 !== 1 ) {
It works because
!! makes a boolean from any value (true if the objects evaluates as true in if(value))
when adding booleans you get 1 for true and 0 for false.
You mentioned in your comment that this is coming from a commander options object.
You can do this more elegantly using Lodash:
if (_(options).values().compact().size() === 1)
If you only want to count a subset of the options, you can insert
.pick('a', 'b', 'c')
if ([opt1, opt2, opt3].reduce(function(x, y) { return x + !!y }, 0) == 1) {
// exactly one
};
ECMAScript 5 reduce function.
I think you are being too clever, what's wrong with:
var optionsSelected = 0;
if( opt1 ) optionsSelected++;
if( opt2 ) optionsSelected++;
if( opt3 ) optionsSelected++;
if( optionsSelected !== 1 ) {
throw new Error("Exactly one option must be set");
}
Of course I can play the clever game too:
if( opts.filter(Boolean).length !== 1 ) {
throw new Error("Exactly one option must be set");
}
#spudly is on the right track, but it could be a little more compact:
if( [opt1,opt2,opt3].filter(function(x){return x}).length!==1 ) {
throw new Error("Exactly one option must be set");
}
See ES5's filter method for more information.

Javascript OR in an IF statement

I am trying to make an if statement in javascript that will do something if the variable does not equal one of a few different things. I have been trying many different variations of the OR operator, but I cant get it to work.
if(var != "One" || "Two" || "Three"){
// Do Something
}
Any ideas? Thanks!
Update:
I have tried this before:
if(var != "One" || var != "Two" || var != "Three"){
// Do Something
}
For some reason it does not work. My variable is pulling information from the DOM i dont know if that would effect this.
Actual Code
// Gets Value of the Field (Drop Down box)
var itemtype = document.forms[0].elements['itemtype' + i];
if(itemtype.value != "Silverware" || itemtype.value != "Gold Coins" || itemtype.value != "Silver Coins"){
// Do Something
}
Your expression is always true, you need:
if(!(myVar == "One" || myVar == "Two" || myVar == "Three")) {
// myVar is not One, Two or Three
}
Or:
if ((myVar != "One") && (myVar != "Two") && (myVar != "Three")) {
// myVar is not One, Two or Three
}
And, for shortness:
if (!/One|Two|Three/.test(myVar)) {
// myVar is not One, Two or Three
}
// Or:
if (!myVar.match("One|Two|Three")) {
// ...
}
More info:
De Morgan's Laws
Edit: If you go for the last approaches, since the code you posted seems to be part of a loop, I would recommend you to create the regular expression outside the loop, and use the RegExp.prototype.test method rather than String.prototype.match, also you might want to care about word boundaries, i.e. "noOne" will match "One" without them...
Assuming you mean "val does not equal One or Two or Three" then De Morgan's Theorem applies:
if ((val != "One") && (val != "Two") && (val != "Three")) {
// Do something...
}
For a shorter way to do it, try this format (copied from http://snook.ca/archives/javascript/testing_for_a_v):
if(name in {'bobby':'', 'sue':'','smith':''}) { ... }
or
function oc(a)
{
var o = {};
for(var i=0;i<a.length;i++)
{
o[a[i]]='';
}
return o;
}
if( name in oc(['bobby', 'sue','smith']) ) { ... }
The method mentioned by Mike will work fine for just 3 values, but if you want to extend it to n values, your if blocks will rapidly get ugly. Firefox 1.5+ and IE 8 have an Array.indexOf method you can use like so:
if(["One","Two","Test"].indexOf(myVar)!=-1)
{
//do stuff
}
To support this method on IE<=7, you could define a method called Array.hasElement() like so:
Array.prototype.hasElement = function hasElement(someElement)
{
for(var i=0;i<this.length;i++)
{
if(this[i]==someElement)
return true;
}
return false;
}
And then call it like so:
if(!["One","Two","Three"].hasElement(myVar))
{
//do stuff
}
Note: only tested in Firefox, where this works perfectly.
In addition to expanding the expression into three clauses, I think you'd better name your variable something other than var. In JavaScript, var is a keyword. Most browsers aren't going to alert you to this error.
Alternate way using an array:
var selected = ['Silverware', 'Gold Coins', 'Silver Coins'];
if ( selected.indexOf( el.value ) != -1 ) {
// do something if it *was* found in the array of strings.
}
Note: indexOf isnt a native method, grab the snippet here for IE:
https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/IndexOf

Is a JavaScript try-catch ignoring an expected occasional error bad practice?

In JavaScript is it wrong to use a try-catch block and ignore the error rather than test many attributes in the block for null?
try{
if(myInfo.person.name == newInfo.person.name
&& myInfo.person.address.street == newInfo.person.address.street
&& myInfo.person.address.zip == newInfo.person.address.zip) {
this.setAddress(newInfo);
}
} catch(e) {} // ignore missing args
If you expect a particular condition, your code will be easier to maintain if you explicitly test for it. I would write the above as something like
if( myInfo && newInfo
&& myInfo.person && newInfo.person
&& myInfo.person.address && newInfo.person.address
&& ( myInfo.person.name == newInfo.person.name
&& myInfo.person.address.street == newInfo.person.address.street
&& myInfo.person.address.zip == newInfo.person.address.zip
)
)
{
this.setAddress(newInfo);
}
This makes the effect much clearer - for instance, suppose newInfo is all filled out, but parts of myInfo are missing? Perhaps you actually want setAddress() to be called in that case? If so, you'll need to change that logic!
Yes. For one, an exception could be thrown for any number of reasons besides missing arguments. The catch-all would hide those cases which probably isn't desired.
I would think that if you're going to catch the exception then do something with it. Otherwise, let it bubble up so a higher level can handle it in some way (even if it's just the browser reporting the error to you).
On a related note, in IE, even though the specs say you can, you can not use a try/finally combination. In order for your "finally" to execute, you must have a catch block defined, even if it is empty.
//this will [NOT] do the reset in Internet Explorer
try{
doErrorProneAction();
} finally {
//clean up
this.reset();
}
//this [WILL] do the reset in Internet Explorer
try{
doErrorProneAction();
} catch(ex){
//do nothing
} finally {
//clean up
this.reset();
}
You could always write a helper function to do the checking for you:
function pathEquals(obj1, obj2, path)
{
var properties = path.split(".");
for (var i = 0, l = properties.length; i < l; i++)
{
var property = properties[i];
if (obj1 === null || typeof obj1[property] == "undefined" ||
obj2 === null || typeof obj2[property] == "undefined")
{
return false;
}
obj1 = obj1[property];
obj2 = obj2[property];
}
return (obj1 === obj2);
}
if (pathEquals(myInfo, newInfo, "person.name") &&
pathEquals(myInfo, newInfo, "person.address.street") &&
pathEquals(myInfo, newInfo, "person.address.zip"))
{
this.setAddress(newInfo);
}
For the example given I would say it was bad practice. There are instances however where it may be more efficient to simply trap for an expected error. Validating the format of a string before casting it as a GUID would be a good example.

Categories