JavaScript: Pass conditional condition as value - javascript

I have a function that finds elements using jQuery. I then take that element, get a value from it and assign it to a variable. However, sometimes an element will not exist on the document and the function will return undefined. This is expected and if the element returned is undefined then the variable that gets assigned should also be undefined. Since I am using this function to assign variables many times I wanted to make it neat and on one line. I thought using conditionals might be a solution, however, doing this leads to really messy long lines and needing to call the function twice:
let html = $(response.body);
let language = getRowElementFromText('Language', html) ? getRowElementFromText('Language', html).find('a').text() : undefined;
let pages = getRowElementFromText('Page', html) ? parseInt(getRowElementFromText('Page', html).text()) : undefined;
Is it possible to resolve those issues and somehow pass the condition of the conditional to be used as the value? For example, in this pesudo code this would be the value of the conditional:
let html = $(response.body);
let language = getRowElementFromText('Language', html) ? this.find('a').text() : undefined;
let pages = getRowElementFromText('Page', html) ? parseInt(this.text()) : undefined;
If this not possible is there another more readable way I can accomplish this on one line?

It is/will be with the optional chaining operator that's new in ES2020 (available via transpilation today). the code would look like this (it only helps so much with the parseInt cas):
let language = getRowElementFromText('Language', html)?.find('a').text();
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^
let pageText = getRowElementFromText('Page', html)?.text();
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^
let pages = pageText ? parseInt(pageText) : undefined;
language would be undefined if the element wasn't found.
If you can't use it (because your target environments don't have it yet and you're not transpiling), you could pass in a function that getRowElementFromText will call if the element is found:
let html = $(response.body);
let language = getRowElementFromText('Language', html, el => el.find('a').text());
let pages = getRowElementFromText('Page', html, el => parseInt(el.text()));
getRowElementFromText would look like this:
function getRowElementFromText(text, html, callback) {
const result = /*...existing logic...*/;
return result === undefined ? undefined : callback(result);
}
Or perhaps like this (callback is optional):
function getRowElementFromText(text, html, callback) {
const result = /*...existing logic...*/;
return !callback || result === undefined ? result : callback(result);
}
Note: In the above I'm assuming getRowElementFromText does actually return undefined in some cases ("not found"), and returns a jQuery object in other cases (found). I flag this up because if it always returns a jQuery object (which may be empty), jQuery objects are never falsy (not even empty ones).

Add the following function to jQuery prototype. It receives a fallback string as an argument and when element is not found, returns fallback string:
$.prototype.getText = function(fallback = undefined) {
return this.length > 0 ? this.text() : fallback
}
console.log($("#foo").getText())
console.log($("#bar").getText("If the element does not exists, returns given fallback string"))
console.log(`Fallback string default value is: ${$("#baz").getText()}`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label id="foo">If the element exists, return its inner text</label>
Usage:
$(selector).getText(fallbackString);

Related

Prevent repeating and writing shorter

I check three elements for their availability. If they are defined, I do the same actions for each element. I would like to avoid the code being repeated. How can I write this in a shorter, more efficient way?
const studentElem = component.find("students").getElement();
if (studentElem != undefined) {
let studentNoice = studentElem.innerText;
studentNoice = studentNoice.replace('classNo', 'Reg No.');
studentElem.innerHTML = studentNoice;
}
const staffElem = component.find("staff").getElement();
if (staffElem != undefined) {
let staffNoice = staffElem.innerText;
staffNoice = staffNoice.replace('staffNo', 'Staff Rec.');
staffElem.innerHTML = staffNoice;
}
const parentElem = component.find("parents").getElement();
if (parentElem != undefined) {
let parentNoice = parentElem.innerText;
parentNoice = parentNoice.replace('ParentID', 'P-ID.');
parentElem.innerHTML = parentNoice;
}
You've got the right idea, this should be condensed. You can do this by passing the variable data into a function as arguments and making decisions based on those argument values.
You also don't need to explicitly check for !=undefinded. You can just check for the existence of the element if(elem). And, don't use .innerHTML when the string you are working with doesn't contain any HTML as .innerHTML has security and performance implications. Use .textContent instead.
From MDN:
It is recommended that you do not use innerHTML when inserting plain
text; instead, use Node.textContent. This doesn't parse the passed
content as HTML, but instead inserts it as raw text.
function foo(role, find, replace){
const elem = component.find(role).getElement();
// No need to check for !=undefined because that essentially means
// you are checking for a "truthy" value, which is what an if condition
// checks for by default, so checking the variable itself forces the
// value to be converted to a Boolean. If the element exists, the Boolean
// becomes true and if not, it is false.
if (elem) {
// Setting up variables that are only used once is a bit of a waste
// Just do the work here and there's less code to write and maintain
elem.textContent = elem.textContent.replace(find, replace);
}
}
// You then call the function and pass the variable data
foo("students", 'classNo', 'Reg No.');
foo("staff", 'staffNo', 'Staff Rec.');
foo("parents", 'ParentID', 'P-ID.');
Make a function!
function yourBuisnessLogic(componentName, subStr, newSubStr){
const elem = component.find(componentName).getElement();
if (elem != undefined) {
let notice = elem.innerText;
notice = notice.replace(subStr, newSubStr);
elem.innerHTML = notice;
}
}
and function calls :
yourBuisnessLogic("students",'classNo', 'Reg No.');
yourBuisnessLogic("staff",'staffNo', 'Staff Rec.');
yourBuisnessLogic("parents",'ParentID', 'P-ID.');

Setting a Javascript if statement with 2 requirements to one line

var status = result.locations[index].status;
var operator = result.locations[index].operator;
var original = result.locations[index].original;
var produced = result.locations[index].produced;
var href = result.locations[index].more;
I have the above which each need to be an if statement to check if there is content and my output is the below code.
if (result.locations[index] && result.locations[index].status){
var status = result.locations[index].status;
} else {
var status = '';
}
I would need to reproduce this per line from the code at the top of the post. What would be the best method to simplify each down to keep the code neater and not produce 5 lines of if statement when 1 or 2 would do.
var status = (result.locations[index] && result.locations[index].status ? result.locations[index].status : '');
Not sure why you want to, but:
var status = (result.locations[index] && result.locations[index].status) ? result.locations[index].status : ""
Your problem is trying to access a property of a "deep" javascript object using its path.
This is a common question :
Javascript: Get deep value from object by passing path to it as string
Accessing nested JavaScript objects with string key
There is no built-in way to do this in javascript.
There are plenty of libraries to do that, for example, with selectn, this would become something like (I have not tested it, so I don't know if the index part will work, but you get the idea) :
var status = selectn("locations." + index + ".status", result) || ''
If the structure of your objects is always the one above (that is, the property is just at one level of depth), and you're not expecting 'falsy', you could simply write the 'test' function yourself :
function safeGet(instance, propertyName, defaultValue) {
// As pointed by AlexK, this will not work
// if instance[propertyName] can be anything Falsy ("", 0, etc...)
// If it's possible, get a library that will do
// the full series of insane checks for you ;)
if (instance && instance[propertyName)) {
return instance[propertyName];
} else {
return defaultValue;
}
}
var location = result.locations[index]; // Potentially undefined, but safeGet will deal with it
var status = safeGet(location, "status", "");
var operator = safeGet(location, "operator", "DEFAULT_OPERATOR");
...
var status = result.locations[index] && result.locations[index].status || '';
However, better maje sure before, if result.locations[index] exists... else do whatever is to be done in your code..

My function returns empty. why?

I know that this is a basic question but I am stuck with it somewhere in my code. I got that code from somewhere but now I am modifying it according to my need.
What does jQuery('#selector') do? In my code it always return empty.
Here is my code
query: function (selector, context) {
var ret = {}, that = this, jqEls = "", i = 0;
if(context && context.find) {
jqEls = context.find(selector);
} else {
jqEls = jQuery(selector);
}
ret = jqEls.get();
ret.length = jqEls.length;
ret.query = function (sel) {
return that.query(sel, jqEls);
}
return ret;
}
when I call this query function then I pass selector as parameter. When I do console.log(selector) it does have all the selectors which I need in this function. But the problem is on this line jqEls = jQuery(selector);. when I do console.log(jqEls) after this it returns empty thus the whole function returns empty.
Can I use something different then this to make it work?
jquery('#selector') is the equivalent of document.getElementById('selector'). If there is no DOM node with an id of selector, you get an empty result.
e.g.
<div id="selector">...</div>
would return the dom node corresponding to this div. Do you have jquery loaded?
jQuery(selector) is looking for a DOM element that meets the selector criteria.
$('#example') == jQuery('#example')
Both will look for something with id "example"
$(selector).get() will return undefined if no element is found. This is why your function returns undefined. To fix this, you could use a default value if there is no element found:
ret = jqEls.length ? jqEls.get() : {};
This way your function will always return an object that has your length and query properties, but it will not have an element if jQuery did not find one.
After reading your code I have a question : do you put the # in your variable selector ?
A solution to solve this by replacing the bad line by jqEls = jQuery("#" + selector);
If the problem isn't due to that can you say the type of selector ? string ? object ? jQueryObject ?

Creating a custom jquery selector (not filter/pseudo selector)

I wanted to create a custom selector similar to Basic CSS Selectors, not like those Pseudo selectors which starts with a colon (created using $.expr[':']). This is to incorporate the document.elementFromPoint method to jquery.
I wrote a code for that as follows:
$._find = $.find;
$.find = function(query, context, extra, seed ){
var start,end,expr,_ref,top,left;
if(typeof query === 'string'){
start = query.indexOf("(");
end = query.indexOf(")",start);
if(start !== -1 && end !== -1){
expr = "["+query.slice(start+1,end)+"]";
_ref = $.parseJSON(expr);top=_ref[0];left=_ref[1];
console.log(document.elementFromPoint(top,left));
return $([document.elementFromPoint(top,left)]);
}
}
return $._find.apply(null,[query, context, extra, seed]);
};
It works for the normal queries. but when a code $('(10,20)') is executed, an empty jquery result is returned.The problem is happening at the line return $([document.elementFromPoint(top,left)]); as the previous line happens to give a valid DOM Element.
Any comment is welcome.
PS: I'm not looking for a Pseudo-Selector,Thanks in advance

jquery/javascript missing ) after formal parameters

Hi there I have this code:
function getFullnameDetails(mainHeight,upperstyle,type=''){
setTimeout(function(){fullnameCenter(mainHeight,upperstyle,type='')},2000);
}
function fullnameCenter(mainHeight,upperstyle,type=''){
var distOfMainAndUpper = mainHeight - upperstyle;
var mainHalfHeight = mainHeight/2;
var imageHeight = jQuery("img[rel='fullname']").height(); //there is a delay
var imageHalfHeight = imageHeight/2;
var fromImageTopToMainHalf = mainHalfHeight - imageHeight;
var position = imageHalfHeight+fromImageTopToMainHalf-distOfMainAndUpper;
if(type == 'big'){ jQuery("#temp .test1").css("bottom",position+"px"); }
else { jQuery(".test1").css("bottom",position+"px"); }
}
It says that I'm missing ) after formal parameters.
This happens on this line:
function getFullnameDetails(mainHeight,upperstyle,type=''){ //IT HAPPENS HERE! :)
setTimeout(function(){fullnameCenter(mainHeight,upperstyle,type='')},2000);
}
What am I doing wrong here.
Thanks in advance for the help :)
Javascript does not support default function parameter values.
You can do things like this (but be wary of unintended 'falsey' values):
function getFullnameDetails(mainHeight,upperstyle,type){
type = type || ''; // if type is 'falsey' (null, undefined, empty string) assign ''
//...
}
As other answers have pointed out, JavaScript doesn't support default values in the function parameters. However, as also pointed out in the other solutions, using an inline || won't work when falsey values are passed (eg null, 0, '', NaN, false). A better solution is to check to see if the argument is undefined:
type = typeof(type) !== 'undefined' ? type : '';
or, more generally:
argument_name = typeof(argument_name) !== 'undefined' ? argument_name : default_value;
Of course, this means that passing undefined to a function will mean that the default value is used instead - but that should be pretty uncommon.
Are you trying to use like a default parameter or something?
type=''
That is not valid -- you can't have an equal sign in the parameter list of a function like that.

Categories