JavaScript: Pass conditional condition as value

Viewed 242

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?

2 Answers

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