Trouble setting JavaScript variable of object using ternary operator - javascript

Hi Guys I’m having trouble trying to set a variable (val) to be one of 2 possible object attributes. The below code explains what I’m trying to do.
function myFnct(elem, imgSrcType) {
var val = imgSrcType == "bg" ? elem.style.backgroundImage : elem.src;
val = 'image.jpg'
}
I’m using a ternary operator to try and avoid having to write:
if (imgSrcType === "bg") {
elem.style.backgroundImage = "url('image.jpg')";
}
else {
elem.src = "image.jpg";
}
Basically the ‘val’ variable is not getting set correctly as I guess its something to do with elem object. I’m trying to avoid using the if statement as I will need to use it a few times within the function. And I’m trying to keep is as DRY as possible.
Any help getting it to work with the ternary operator method would be awesome!

if (imgSrcType === "bg") {
elem.style.backgroundImage = "url('image.jpg')";
}
else {
elem.src = "image.jpg";
}
ugly but working rewrite:
void (
imgSrcType === 'bg'
&& (elem.style.backgroundImage = "url('image.jpg')")
|| (elem.src = "image.jpg")
);
Equals:
void (
imgSrcType === 'bg'
? (elem.style.backgroundImage = "url('image.jpg')")
: (elem.src = "image.jpg")
);
So by adding parentheses (elem.src = "image.jpg") you can do the assignment. You can also use a comma to return something in a value assignment.
Using this knowledge, you could rewrite myFnct:
function myFnct(elem, imgSrcType) {
var val = (
void( imgSrcType == "bg"
? (elem.style.backgroundImage = "url('image.jpg')")
: (elem.src = "image.jpg") ), //<= ternary done - comma
'image.jpg'
);
//now val = 'image.jpg'
}
Note: this is all about what is possible. If you need/want to keep your code readable, using the if ... else statement is the better option.

Ternary operations can only assign their outcome to a single variable. They are useful if you are setting that single variable to different values depending on the result of a boolean expression. Since you are trying to assign the image URL to either the background-image or to the source, you cannot use a simple ternary operation. The other answers are using pretty complex/quasi-obfuscated code to accomplish what could be done with a simple if/else statement. Your code - especially for such a simple operation - should be easy to read. So, I recommend just sticking with the following:
function setImage(elem, imgSrcType)
{
var imgURL = "image.jpg";
if(imgSrcType == "bg")
{
elem.style.backgroundImage = "url('" + imgURL + "')";
}
else
{
elem.src = imgURL;
}
}

Related

How to remove my nested ternary expressions in my code?

I would like to clean up my code but I can't. I would like to get rid of the nested ternary expressions. I work with react js 17.0.2. Do you have any ideas to help me?
const buildNewFilters = (query, filtersIndex: Array<string>) => {
const newFilters = {};
for (let i = 0; i < filtersIndex.length; i++) {
newFilters[filtersIndex[i]] = router.query[filtersIndex[i]] ? typeof router.query[filtersIndex[i]] == ('string' || 'number') ? [router.query[filtersIndex[i]]] : router.query[filtersIndex[i]] : undefined
if (filtersIndex[i] === 'designers' && newFilters.designers) {
newFilters.designers = newFilters.designers.map(designer => parseInt(designer));
}
}
return newFilters;
};
if (router.query[filtersIndex[i]]) {
if (typeof router.query[filtersIndex[i]] == ("string" || "number")) {
newFilters[filtersIndex[i]] = [router.query[filtersIndex[i]]];
} else {
newFilters[filtersIndex[i]] = router.query[filtersIndex[i]];
}
} else {
newFilters[filtersIndex[i]] = undefined;
}
If you are not used to working with ternary operators, I understand that it's not easy to refactor the code. First, you can read about what ternary expressions are and try them out in the Mozilla Documentation about ternary operators. Basically the part of the code before the ? evaluates to either true or false and if it evaluates to true, the part before the subsequent : is executed (in your case assigned to the variable newFilters[filtersIndex[i]], otherwise the part after the : will be assigned to the variable.
My tip would be to put the line you want to refactor in a text editor to experiment with it, and add line breaks and/or tabs after the ? and : signs to see the structure better and to see what is happening at each step.

Can you make this JavaScript code smaller or more efficient?

Using pure vanilla JavaScript can you make this smaller? Or even more efficient?
It's a "copy" of Jquery's '$' function. Though this works different, here is the code:
function $(id,from = document) {
if(!'#.<'.includes(id.charAt(0))) id = '#' + id;
if (id.charAt(0) == '<') id = id.charAt(id.length-1) == '>' ? id.substring(1,id.length-1) : id.substring(1,id.length);
return from.querySelectorAll(id).length == 1 ? from.querySelectorAll(id)[0] : from.querySelectorAll(id).length == 0 ? false : from.querySelectorAll(id);
}
At least you can extract the result of from.querySelectorAll(id) into a variable instead of evaluating it multiple times.
And in another, just as Barmar suggested, cache the result of this function invocation somewhere out of it and keep this function simple and stupid(KISS).

javascript ternary if using condition's value as the outcome

In PHP, say if I have code like this:
$aValue = functionThatReturnsAValue(); // the function might return a string or null;
$anotherValue = $aValue ? process($aValue) : null;
only for brievity (IDK is this a good practice or not and also regarding the performance, etc), I used to change the code like so:
$anotherValue = ($aValue = functionThatReturnsAValue()) ? process($aValue) : null;
My questions are:
Is this style even a good practice?
How can I use this with JavaScript? I wrote with same style but got error.
Thank you.
You could use a logical AND && and process only a given value.
If aValue is null or a falsy value, this value is assigned to anotherValue, otherwise, it takes the value of process(aValue).
anotherValue = (aValue = functionThatReturnsAValue()) && process(aValue);
Since aValue is the value returned from executing functionThatReturnsAValue(), You can try this
var anotherValue = (functionThatReturnsAValue()) ? process(functionThatReturnsAValue()) : null;

jQuery to Vanilla JS - querySelector issues

I'm going through some code and working to change all of the jQuery to vanilla JS. However there is one section and I keep getting an error in my console that says either:
TypeError: document.querySelectorAll(...).toggle is not a function pr
TypeError: document.querySelectorAll(...) is null
Below is my code, the top part you can see is where I am trying to change the jquery to vanilla js (I have commented out the jquery) :
console.log(shipmentNumbers);
for (let i = 0; i < shipmentNumbers.length; i += 1) {
let sNumber = shipmentNumbers[i];
function getHistory(event) {
console.log(event);
document.querySelectorAll('#shipment' + sNumber + 'tr.show-history' + sNumber).toggle();
// $('#shipment' + sNumber + ' tr.show-history' + sNumber).toggle();
document.getElementsByClassName('overlay-line' + sNumber).style.display = 'table-row';
// $('.overlay-line' + sNumber).css({
// "display": "table-row"
// });
if (flag == false) {
let shipmentNumber = event.currentTarget.id.replace('status', '');
console.log('shipmentNumber=', shipmentNumber);
callHistoryApi(clientId, shipmentNumber);
$(this).find('.expand' + sNumber).html("▼");
flag = true;
} else {
$(this).find('.expand' + sNumber).html("►");
$('.overlay-line' + sNumber).css({
"display": "none"
});
flag = false;
}
}
Can someone explain why this isn't working, and how I can get it working using vanilla js?
I find that writing these two functions can really help when moving from jQuery to native JS.
function domEach(selector, handler, context) {
return Array.from(document.querySelectorAll(selector), handler, context);
}
// If you get a TypeError "Array.from" is not a function, use the polyfill
// found on MPN.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
This gets you around issues where you relied on implicit loops that jQuery uses.
// Instead of these:
document.querySelectorAll('#shipment' + sNumber + 'tr.show-history' + sNumber).toggle();
document.getElementsByClassName('overlay-line' + sNumber).style.display = 'table-row';
// Use these:
domEach('#shipment' + sNumber + 'tr.show-history' + sNumber, function (tr) {
tr.style.display = tr.style.display === "none"
? ""
: "none";
});
domEach('.overlay-line' + sNumber, function (el) {
el.style.display = 'table-row';
});
For a list of techniques to use instead of the jQuery functions, you can check You Might Not Need jQuery
Edit: more information about the code above
jQuery uses implicit loops. That is, when you do this:
$("#one").addClass("two");
jQuery does this behind the scenes:
var elements = document.querySelectorAll("#one");
var i = 0;
var il = elements.length;
while (i < il) {
elements[i].classList.add("two");
i += 1;
}
This leads to some confusion when going from jQuery to vanilla JavaScript since you have to manually loop over the results of querySelectorAll.
Array.from will loop over an array or array-like structure. querySelectorAll will return a NodeList - this is an array-like structure (it has numerical indicies and a length property). The domEach function allows us to pass a CSS selector to the function and will loop over the results of finding matching elements.
The ? : syntax is called a ternary operator. It's a short-cut for if ... else.
// Ternary operator
tr.style.display = tr.style.display === "none"
? ""
: "none";
// Equivalent if/else statements
if (tr.style.display === "none") {
tr.style.display = "";
} else {
tr.style.display = "none";
}
I hope that helps clarify things.
You must add check in whenever you do this, as .querySelctor/All() is going to return "null" if no elements are found.
var myCollection = document.querySelectorAll("selector");
if (myCollection.length > 0){
Array.prototype.forEach.call(myCollenction, function(element){
if(typeof element.toggle === "function"){
element.toggle();
}
})
}
More or less this will help you achieve your goal. However if you don't have method "toggle" defined on your elements - nothing will happen. :)

Avoiding having to write the same word over and over again

I'm very new to javascript so this question might sound stupid. But what is the correct syntax of replacing certain words inside variables and functions. For example, I have this function:
function posTelegram(p){
var data = telegramData;
$("#hotspotTelegram").css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$("#hotspotTelegram").hide()
} else {
$("#hotspotTelegram").show()
}
};
There is the word "telegram" repeating a lot and every time I make a new hotspot I'm manually inserting the word to replace "telegram" in each line. What would be a smarter way of writing that code so that I only need to write "telegram" once?
Group similar / related data in to data structures instead of having a variable for each bit.
Cache results of calling jQuery
Use an argument
function posGeneral(p, word){
// Don't have a variable for each of these, make them properties of an object
var data = generalDataThing[word].data;
// Don't search the DOM for the same thing over and over, use a variable
var hotspot = $("#hotspot" + word);
hotspot.css("left", generalDataThing[word].xpos[p] +"px");
if (p < data[0] || p > data[1]) {
hotspot.hide()
} else {
hotspot.show()
}
};
You can't always avoid this kind of repetition (this is general to all programing languages).
Sometimes, you can make generic functions or generic classes, for example a class which would embed all your data :
Thing = function(key, xpos) {
this.$element = $('#hotspot'+key);
this.xpos = xpos;
};
Thing.prototype.pos = function (p, data) {
this.$element.css("left", this.xpos[p] +"px");
if (p < this.data[0] || p > this.data[1]) {
this.$element.hide()
} else {
this.$element.show()
}
};
And we could imagine that this could be called like this :
var telegramThing = new Thing('telegram', xposTelegram);
...
telegramThing.pos(p, data);
But it's really hard to make a more concrete proposition without more information regarding your exact problem.
I recommend you read a little about OOP and javascript, as it may help you make complex programs more clear, simple, and easier to maintain.
For example, using a Thing class here would enable
not defining more than once the "#hotspotTelegram" string in your code
reusing the logic and avoid making the same code with another thing than "telegram"
not having the Thing logic in your main application logic (usually in another Thing.js file)
But don't abstract too much, it would have the opposite effects. And if you don't use objects, try to keep meaningful variable names.
var t = "Telegram";
var $_tg = $('#hotspotTelegram');
$_tg.css("left", "xpos"+t[p] + "px"); // not sure about this line, lol
$_tg.hide();
$_tg.show();
etc.
you can create a selector as variable, something like this
function posTelegram(p){
var data = telegramData;
var $sel = $("#hotspotTelegram");
$sel.css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$sel.hide()
} else {
$sel.show()
}
};

Categories

Resources