Combine if/else into one statement? (Javascript) - javascript

function changeButton () {
if (event.currentTarget.className == 'btnRed') {
event.currentTarget.className = 'btnGreen';
} else {
event.currentTarget.className = 'btnRed';
}
}
Let's say I have the above code. I have seen similar codes written that would combine these two, but I don't really remember how it did it. It was something like className = (btnGreen | btnRed).
I am very sorry for the vagueness of this question.

You can use the ternary operator (condition) ? (true) : (false)
event.currentTarget.className = event.currentTarget.className == 'btnRed' ? 'btnGreen' : 'btnRed';
I would go a little bit further, extract the strings into variables to remove the ability to mistype them across the solution. And refactor event.currentTarget into a variable.
var RED_BUTTON_CLASS = 'btnRed',
GREEN_BUTTON_CLASS = 'btnGreen';
var currentTarget = event.currentTarget;
currentTarget.className = currentTarget.className == RED_BUTTON_CLASS ? GREEN_BUTTON_CLASS : RED_BUTTON_CLASS;
This I feel will just make it easier in the long run, completely optional
Edit
So adding extra information from what Jan said.
var RED_BUTTON_CLASS = 'btnRed',
GREEN_BUTTON_CLASS = 'btnGreen';
These probably describe a state, so you could better name them:
var ERROR_BUTTON_CLASS = 'btnRed',
OK_BUTTON_CLASS = 'btnGreen';

Related

How to Efficiently Utilize Objects in Conjunction with HTML Elements

I am getting pretty deep into an employee talent management system website and I am finding out really quick why OOP is so talked about. Being able to efficiently utilize it would change that game for me and make my codebase much more maintainable. I am currently working on a dropdown menu section, and while I have implemented OOP, I can tell from a mile away it is not "good code." It gets the job done but is very messy. I will have an example of the Object and Implementation down below. I have tried watching a few videos on implementing OOP in an existing project, but I keep coming up dry. I understand the basic concepts, but I am not sure how to implement these ideas in a real world application.
Here are some sticking points I am having:
I am not confident I instantiated my objects correctly/efficiently. I have a list of toggleable menus, each representing a candidate in the portal. Well, I created an object for each menu using a for loop. This worked, but it felt unintuitive. When I wanted to attach click events to these menus, I had to loop through them a second time to attached to objects methods as click events.
I felt I had a lot of 'if' statements in my Object. This is because I see myself using the object again in the future, but I do not think each menu will have all the same features. For example, this specific menu had a fade in animation when clicked. But, I doubt every toggleable menu will have this feature. I don't know, I just felt it made the code way more difficult to navigate.
I did not like having to loop through my menu elements and attach methods as click events. This made me have to run an additional for loop and felt 'off'. I was tempted to handle all the event attachments inside the object itself, but I wasn't sure if this is bad practice. It seemed like a good idea and would save a lot of code, but I'm not sure.
Long story short, I can see myself using OOP for forms, buttons, toggleable menus, navbars, (ect.) all over the place. The problem is I am not sure how to actually carry out the process of handling HTML elements and converting them to Objects in a clear and concise manner.
I looked into some design patterns on refactoring.guru, but I felt these concepts were past the scope of what I am trying to do. They all felt like more advanced concepts to take on after getting a solid grip on objects. It was helpful and I really liked the builder pattern. I am currently working on a branch which implements the builder pattern into my current scenario.
Any thoughts, advice, or direction?
Here is an example of the Object and it's Implementation:
TOGGLE MENU OBJECT
class ToggleMenu {
constructor(wrapper, menu, openIcon, closeIcon, title, hiddenMenu) {
this.wrapper = wrapper
this.menu = menu
this.openIcon = openIcon
this.closeIcon = closeIcon
this.title = title
this.hiddenMenu = hiddenMenu
this.toggled = false
}
props() {
console.log(this.menu)
console.log(this.openIcon)
console.log(this.closeIcon)
console.log(this.title)
console.log(this.hiddenMenu)
console.log(this.toggled)
}
openHiddenMenu(config) {
if (config == undefined){
config = {}
}
if (config.menuBackGroundColor !== undefined){
this.menu.style.backgroundColor = config.menuBackGroundColor
}
if (config.titleColor !== undefined){
this.title.style.color = config.titleColor
}
if (config.hiddenMenuDisplay !== undefined){
this.hiddenMenu.style.display = config.hiddenMenuDisplay
}
if (config.hiddenMenuAnimation !== undefined){
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
}
if(config.menuAnimation !== undefined){
this.menu.style.animationName = config.menuAnimation
}
this.openIcon.style.display = 'none'
this.closeIcon.style.display = 'block'
this.toggled = true
}
closeHiddenMenu(config){
if (config == undefined){
config = {}
}
if (config.menuAnimation !== undefined && this.toggled == true){
this.menu.style.animationName = config.menuAnimation
}
this.menu.style.backgroundColor = ''
this.openIcon.style.display = ''
this.closeIcon.style.display = ''
this.title.style.color = ''
this.hiddenMenu.style.display = ''
this.toggled = false
}
}
IMPLEMENTATION
const initCandidateMenus = () => {
let candidateToggleMenus = document.getElementsByClassName('candidate-toggle-menu')
let candidateToggleWrappers = document.getElementsByClassName('candidate-toggle-menu-wrapper')
let hiddenMenus = document.getElementsByClassName('hidden-candidate-menu')
let toggleMenus = []
//collecting toggle menu objects
for (x = 0; x < candidateToggleMenus.length; x++){
let toggleMenu = new ToggleMenu(
candidateToggleWrappers[x],
candidateToggleMenus[x],
candidateToggleMenus[x].getElementsByClassName('candidate-open-icon')[0],
candidateToggleMenus[x].getElementsByClassName('candidate-close-icon')[0],
candidateToggleMenus[x].getElementsByClassName('candidate-name')[0],
hiddenMenus[x],
)
toggleMenus.push(toggleMenu)
}
//looping through toggle menu objects
for (x = 0; x < toggleMenus.length; x++){
let currentMenu = toggleMenus[x]
currentMenu.openIcon.addEventListener('click', () => {
//closing all other toggle menus
for (y = 0; y < toggleMenus.length; y++){
toggleMenus[y].closeHiddenMenu()
}
//opening current menu
currentMenu.openHiddenMenu({
menuBackGroundColor: 'var(--main-clr)',
titleColor: 'var(--white)',
menuAnimation: 'fade-title-color',
hiddenMenuDisplay: 'flex',
hiddenMenuAnimation: 'open-hidden-menu'
})
})
currentMenu.closeIcon.addEventListener('click', () => {
//closing current menu
currentMenu.closeHiddenMenu()
})
}
}
ok this isn’t really an answer, but more of just advice that I need to use code for.
I noticed that you didn’t implement a lot of common features that can reduce lines and characters, so I’ll point them out here
For the class functions, you did this:
openHiddenMenu(config) {
if (config == undefined){
config = {}
}
Which can be simplified to this:
openHiddenMenu(config = {}) {
// Completely remove the entire if statement
Putting an equal sign in the parameter will assign a default value if it isn’t defined.
Another thing is checking if something is undefined:
if (config.menuBackGroundColor !== undefined){
this.menu.style.backgroundColor = config.menuBackGroundColor
}
if (config.titleColor !== undefined){
this.title.style.color = config.titleColor
}
if (config.hiddenMenuDisplay !== undefined){
this.hiddenMenu.style.display = config.hiddenMenuDisplay
}
if (config.hiddenMenuAnimation !== undefined){
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
}
if(config.menuAnimation !== undefined){
this.menu.style.animationName = config.menuAnimation
}
Instead you could remove the ‘!== undefined’ part, because any value that is ‘falsey’ (0, NULL, undefined, NaN) will also return false. So if it has a value and isnt 0, it will return true.
Plus since it’s only 1 line, you can remove brackets:
if (config.menuBackGroundColor)
this.menu.style.backgroundColor = config.menuBackGroundColor
if (config.titleColor)
this.title.style.color = config.titleColor
if (config.hiddenMenuDisplay)
this.hiddenMenu.style.display = config.hiddenMenuDisplay
if (config.hiddenMenuAnimation)
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
if(config.menuAnimation)
this.menu.style.animationName = config.menuAnimation
If you wanted to simplify even further, you could use an or operator ||, so if the value is falsey, it will become the other value. This doubles as a default value.
this.menu.style.backgroundColor = config.menuBackGroundColor || // Default value
this.title.style.color = config.titleColor || // Default value
this.hiddenMenu.style.display = config.hiddenMenuDisplay || // Default value
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation || // Default value
this.menu.style.animationName = config.menuAnimation || // Default value
There are some other things you can do, but this answer is insanely long and I’m writing this late at night. Hope this helps though

JS Ternary functions with multiple conditions?

I have been using a ternary operator in JavaScript to modify the value of an object based on user input. I have the following code, which runs as it should:
var inputOneAns = inputOne == "Yes" ? "517" : "518";
As you can see, I am assigning a numeric string value to inputOneAnswhether a user has inputed "Yes" or "No". However, there may be a case that a user has not selected a value (as it is not required). If this input was left blank, I would like to assign an empty string "" to inputOneAns. Is there a wayf or me to embed an ternary operator inside of another ternary operator? To help clarify, here is the same function that I want to accompolish with my ternary function but with if else statements?
if (inputOne == "Yes"){
var inputOneAns = "517"
}else if (inputOne == "No"{
var inputOneAns = "518"
}else{
var inputOneAns = ""
}
Is it possible to include multiple expressions into a ternary function? Is there a better way to accomplish what I am looking for? Thanks for the tips in advance.
Yes you can go wild nesting ternaries. I find this version to be fairly readable:
var foo = (
bar === 'a' ? 1 : // if
bar === 'b' ? 2 : // else if
bar === 'c' ? 3 : // else if
null // else
);
but that's not a widely shared opinion, and you should probably stick to if/else or switch when working on a team.
Yes, you can use multiple condition in Ternary Operator. Hope this will help you.
var x=20;
var y = x<13 ? "Child" : x<20 ? "Teenage" : x<30 ? "Twenties" : "Old people";
console.log(y);
A switch statement is likely the best choice in a situation like this.
let inputOneAns;
switch(inputOne) {
case "Yes":
inputOneAns = "517";
break;
case "No":
inputOneNas = "518";
break;
default:
inputOneNas = "";
}
If you could do ternary operations beyond 2 conditions, they would become incredibly messy. You can put conditions together, but I've no idea why you would want that - that would be incredibly messy.
The most elegant and clean way is to take advantage of Object literals:
const Switch = (str) => ({
"Yes": "517",
"No": "518",
})[str] || '';
console.log(Switch("Yes")); // 517
console.log(Switch("No")); // 518
console.log(Switch("Non matching value")); // Empty
This has the advantage of being both readable and flexible.
Yeh you can chain them together much like using an else if statement, but it can sometimes be a bit hard to read though, so I tend to split mine over multiple lines.
var inputOneAns = inputOne == 'Yes' ? '517' :
inputOne == 'No' ? '518' : '';
However in this case I would suggest a switch statement seeing as you're comparing the same value for every case.
var r = inputOne == "" ? "" : (
inputOne == "Yes" ? "517" : "518");
Unfortunately JavaScript does not provide a super terse and readable way to do this. Personally I would just use some single-line if statements like this:
var inputOneAns;
if (inputOne === 'Yes') inputOneAns = '517';
if (inputOne === 'No') inputOneAns = '518';
else inputOneAns = '';
Which can be even cleaner if you abstract it into a function (note: no need for else in this case):
function getInputOneAns(inputOne) {
if (inputOne === 'Yes') return '517';
if (inputOne === 'No') return '518';
return '';
}
Personally, I don't really like switch statements for this for two reasons: firstly those extra break statements bloating the code, and secondly, switch statements are very limiting - you can only do simple equality checks, and only against a single variable. Besides, in the case that you know you will be always checking a single string I would favour a simple map object:
var map = { 'Yes': '517', 'No': '518' };
var inputOneAns = map[inputOne] || '';
Yes, and it does provide a cleaner code than switch statement.. with all the breaks..
inputOne == "Yes" ? "517" :
inputOne == "No" ? "518" : inputOneAns = "";
Seems like a classic use for a switch statement:
let inputOneAns = '';
switch(inputOne) {
case 'Yes':
inputOneAns = "517";
break;
case 'No':
inputOneAns = "518";
break;
default:
inputOneAns = "";
}
note you don't actually need the default case, but I find it makes things more readable.

Generalizing two functions which act inversely on two properties

I have these two functions, and I feel there must be an elegant functional way to generalize them.
$scope.togglePick = function(){
let targetPhoto = $scope.photoImport.data[indexService.current];
targetPhoto.pick = !targetPhoto.pick;
if(targetPhoto.reject && targetPhoto.pick) {
targetPhoto.reject = false;
}
}
$scope.toggleReject = function() {
let targetPhoto = $scope.photoImport.data[indexService.current];
targetPhoto.reject = !targetPhoto.reject;
if (targetPhoto.reject && targetPhoto.pick) {
targetPhoto.pick = false;
}
}
I suppose I could pass in string params togglePick = toggle('pick', 'reject') and use [] notation in the toggle function, but this feels like magic strings... even though they're property names, I'm uncertain. I could also have:
function toggle(magicFlag){
let primary = magicFlag ? 'pick' : 'reject';
let secondary = magicFlag ? 'reject' : 'pick';
...
targetPhoto[primary] = !targetPhoto[primary];
...
}
Again, ick, magic flag. Is there a more elegant way to do this than longhand in each function?
The amount of code in these functions and the slight differences makes them fine the way they are. Without working the property string weirdness, I don't see how you really make this generic while remaining readable. Sometimes it's okay not to have the most generic code you possibly can. That line in the sand has to be drawn somewhere.

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..

simplify javascript if statement where conditions are identical except for variable

I apologize if this is a duplicate question. It's such a use-case question that it seems everyone has their own version.
I'm wondering if this can be simplified:
if ($('.taxclass').text().indexOf(tax1)>-1 || $('.taxclass').text().indexOf(tax2)>-1) {}
It's pretty simple as it stands, but you could make it a bit less redundant mainly by getting the elements text only once and reusing the variable:
var text = $('.taxclass').text();
if (text.indexOf(tax1)>-1 || text.indexOf(tax2)>-1) {
}
A further note could be to reduce the traversal of the DOM by using an identifier and looking only for a distinct element (if that suits your needs) instead of every possible thing that has the class taxclass.
var txt = $('.taxclass').text();
if (txt.indexOf(tax1)>-1 || txt.indexOf(tax2)>-1) {}
One super quick way would be not to duplicate $('.taxclass').text()
Try something like
var tax = $('.taxclass').text();
if (tax.indexOf(tax1)>-1 || tax.indexOf(tax2)>-1) {}
You can store $('.taxclass').text() in a variable, or use regex.
var str = $('.taxclass').text();
if (str.indexOf(tax1) > -1 || str.indexOf(tax2) > -1)
// Or with regex
if(/(text1)|(text2)/.test($('.taxclass').text())
{}
Quick and dirty:
text.indexOf(tax1+"~"+tax2)>-1
Functional, works on n strings, but verbose:
[tax1, tax2].some(function(s) { return s.indexOf(text)>-1 })
As a prototype:
String.prototype.foundIn = function() {
var s=this; return Array.prototype.slice.call(arguments).some(function(m)
{return m.indexOf(s)>-1});
};
Usage:
$('.taxclass').text().foundIn(tax1, tax2)
What about:
f = function (x) { return $('.taxclass').text().indexOf(x) > -1; }
if (f(tax1) || f(tax2)) {}

Categories