Dynamically adding cases to a switch - javascript

I need to dynamically add cases to a switch. I want the user to be able to add items and every item needs it's own switch case.

You can use object with callback functions instead:
// you can have initial casses
var callbacks = {
'something': [() => 42]
};
// and you can create new entry with this function
function add(_case, fn) {
callbacks[_case] = callbacks[_case] || [];
callbacks[_case].push(fn);
}
// this function work like switch(value)
// to make the name shorter you can name it `cond` (like in scheme)
function pseudoSwitch(value) {
if (callbacks[value]) {
callbacks[value].forEach(function(fn) {
fn();
});
}
}
and you can add new entry using:
add('something', function() {
// case for something
});
NOTE:
You can also modify this to work a little bit different than the original switch because you can have a function that returns a value and use a switch-like expression (like in Scheme where everything is an expression that returns a value):
const value = cond(42);
Writing this type of pseudoSwitch/cond function is left as an exercise to the reader.
NOTE 2:
By default objects in JavaScript use strings as keys and if you need to use something that can't be easily converted to a string, like objects (that will be converted to [Object object]) then you can use Map object that accepts anything as keys. Note that symbols work differently and they are not converted to a string when used as key in array.

This was the best/simpler solution for my needs:
const customSwitch = [
{
condition: 'case1',
fn() { /* Do stuff if case1 */ },
}, {
condition: 'canBeChangedAnytime',
fn() { /* Do stuff if case2 */ },
},
...adSomeCasesDynamycallyHere,
]
// edit a condition:
customSwitch[0].condition = 'changed';
// use the switch
for (const { condition, fn } of customSwitch) {
if (myValue === condition) {
fn();
break;
}
}
customSwitch, may have the form of an object, which may improve readability. Eg: customSwitch = { myCond: { condition, fn }}
You can click the above snippet to see it working ;)
const customSwitch = [ {
condition: 38,
fn: val => $("body").append(val === 38 ? 'UP' : 'RIGHT') + ' ',
}, {
condition: 40,
fn: val => $("body").append((val === 40 ? 'DOWN' : 'LEFT')+ ' ') ,
}]
$('#option1').click(function () {
customSwitch[0].condition = 38
customSwitch[1].condition = 40
});
$('#option2').click(function () {
customSwitch[0].condition = 39
customSwitch[1].condition = 37
});
$(window).keydown(function (e) {
for (const { condition, fn } of customSwitch) {
if (e.keyCode === condition) {
fn(e.keyCode);
break;
}
}
});
.btn {
cursor:pointer;
padding:5px;
border-radius:5px;
background-color:#3C0;
margin-top:5px;
width:150px;
text-align:center;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Press the four arrow keys:<br>
-if you click <b>option1</b> the switch will recognize UP and DOWN<br>
-if you click <b>option2</b> the switch will recognize LEFT and RIGHT<br>
<div id='option1' class='btn'>OPTION 1</div>
<div id='option2' class='btn'>OPTION 2</div>
<hr>

You can use Object for switch cases. One of the advantages of using Object instead of Array for the case is that it drastically reduces errors caused by wrong indexes in Array. Using Object for cases, you can also extract your case values into another script. This helps for Single Responsibility Principle by concerning you only implementing business logic inside switch cases instead of worrying about maintaining the right case values.
const OP = {
ADD: 'ADD',
MULTIPLY: 'MULTIPLY',
};
const choice = 'ADD';
switch (choice) {
case OP.ADD:
console.log('You chose add');
break;
case OP.MULTIPLY:
console.log('You chose multiply');
break;
default:
console.log('Operation is not defined');
}

Related

How to detect multiple(combination) keyboard press?

I find this code interesting because every time I press a key from my keyboard it alerts it. However how to detect a key with combination
Example
Alt + 1 -- I want to alert something
Alt + 2 -- same here
ETC. Any combination that I want.
I try his code and create an if statement to it
$(document).keypress(function(event){
alert(String.fromCharCode(event.which));
if( String.fromCharCode(event.which) == "a"){
alert("Hi A.");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Credit to Coyod
If you change the event to keydown you will get extra event data that will tell you if any modifier keys are pressed.
Then inside the event callback you can check event.altKey to check if the alt key is currently pressed.
$(document).keydown(function(event) {
if (event.altKey) {
switch (String.fromCharCode(event.which)) {
case 'A':
console.log('Hi A')
break
case 'B':
console.log('Hi B')
break
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Here is a better example that will keep the state of all pressed keys, allowing you to test if more than one key is pressed at the same time. In the callback you have a function checkKeysPressed which takes the keys you wish to add the event for, if those keys are pressed the function will return true.
It's using ES6 syntax, functions and objects, but it could be converted to ES5 easily or just run through babel.
const multipleKeysEventListener = (element, callback) => {
const keysPressed = new Set
const describeKey = e => {
switch(e.which) {
case 18:
return 'ALT'
case 16:
return 'SHIFT'
default:
return String.fromCharCode(e.which)
}
}
const checkPressedKeys = (...keys) =>
keys.every(x =>
keysPressed.has(
typeof(x) === 'number'
? String.fromCharCode(x)
: x
)
)
const down = e => {
keysPressed.add(describeKey(e))
return callback(checkPressedKeys, e)
}
const up = e => {
keysPressed.delete(describeKey(e))
}
$(element).keydown(down)
$(element).keyup(up)
}
multipleKeysEventListener(document, (checkKeysPressed, e) => {
switch (true) {
// you can pass keys
case checkKeysPressed('A', 'B'):
console.log('A and B pressed')
break
// you can pass modifiers
case checkKeysPressed('ALT', 'A'):
console.log('ALT and A pressed')
break
// and you can pass keyCodes
case checkKeysPressed('ALT', 67):
console.log('ALT and C pressed')
break
default:
console.log(String.fromCharCode(e.which))
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Want to add new cases dynamically in switch statement using jquery [duplicate]

I need to dynamically add cases to a switch. I want the user to be able to add items and every item needs it's own switch case.
You can use object with callback functions instead:
// you can have initial casses
var callbacks = {
'something': [() => 42]
};
// and you can create new entry with this function
function add(_case, fn) {
callbacks[_case] = callbacks[_case] || [];
callbacks[_case].push(fn);
}
// this function work like switch(value)
// to make the name shorter you can name it `cond` (like in scheme)
function pseudoSwitch(value) {
if (callbacks[value]) {
callbacks[value].forEach(function(fn) {
fn();
});
}
}
and you can add new entry using:
add('something', function() {
// case for something
});
NOTE:
You can also modify this to work a little bit different than the original switch because you can have a function that returns a value and use a switch-like expression (like in Scheme where everything is an expression that returns a value):
const value = cond(42);
Writing this type of pseudoSwitch/cond function is left as an exercise to the reader.
NOTE 2:
By default objects in JavaScript use strings as keys and if you need to use something that can't be easily converted to a string, like objects (that will be converted to [Object object]) then you can use Map object that accepts anything as keys. Note that symbols work differently and they are not converted to a string when used as key in array.
This was the best/simpler solution for my needs:
const customSwitch = [
{
condition: 'case1',
fn() { /* Do stuff if case1 */ },
}, {
condition: 'canBeChangedAnytime',
fn() { /* Do stuff if case2 */ },
},
...adSomeCasesDynamycallyHere,
]
// edit a condition:
customSwitch[0].condition = 'changed';
// use the switch
for (const { condition, fn } of customSwitch) {
if (myValue === condition) {
fn();
break;
}
}
customSwitch, may have the form of an object, which may improve readability. Eg: customSwitch = { myCond: { condition, fn }}
You can click the above snippet to see it working ;)
const customSwitch = [ {
condition: 38,
fn: val => $("body").append(val === 38 ? 'UP' : 'RIGHT') + ' ',
}, {
condition: 40,
fn: val => $("body").append((val === 40 ? 'DOWN' : 'LEFT')+ ' ') ,
}]
$('#option1').click(function () {
customSwitch[0].condition = 38
customSwitch[1].condition = 40
});
$('#option2').click(function () {
customSwitch[0].condition = 39
customSwitch[1].condition = 37
});
$(window).keydown(function (e) {
for (const { condition, fn } of customSwitch) {
if (e.keyCode === condition) {
fn(e.keyCode);
break;
}
}
});
.btn {
cursor:pointer;
padding:5px;
border-radius:5px;
background-color:#3C0;
margin-top:5px;
width:150px;
text-align:center;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Press the four arrow keys:<br>
-if you click <b>option1</b> the switch will recognize UP and DOWN<br>
-if you click <b>option2</b> the switch will recognize LEFT and RIGHT<br>
<div id='option1' class='btn'>OPTION 1</div>
<div id='option2' class='btn'>OPTION 2</div>
<hr>
You can use Object for switch cases. One of the advantages of using Object instead of Array for the case is that it drastically reduces errors caused by wrong indexes in Array. Using Object for cases, you can also extract your case values into another script. This helps for Single Responsibility Principle by concerning you only implementing business logic inside switch cases instead of worrying about maintaining the right case values.
const OP = {
ADD: 'ADD',
MULTIPLY: 'MULTIPLY',
};
const choice = 'ADD';
switch (choice) {
case OP.ADD:
console.log('You chose add');
break;
case OP.MULTIPLY:
console.log('You chose multiply');
break;
default:
console.log('Operation is not defined');
}

Vanilla JavaScript: Is there a way to toggle multiple CSS-classes in one statement?

I use these JavaScript-code to change classes in my script:
var toggleDirection = function() {
group.classList.toggle('left-to-right');
group.classList.toggle('right-to-left');
}
In my example there a only two classes to change but could be also multiple classes ...
So therefore: Does anyone know a way to write the example less redundant?
No it is not possible using Element.classList API directly. Looking at API you can read:
toggle ( String [, force] ) When only one argument is present: Toggle
class value; i.e., if class exists then remove it, if not, then add
it. When a second argument is present: If the second argument is true,
add specified class value, and if it is false, remove it.
Reference here.
You could potentially write your own "utility" function (in vanilla JS) which does what you want, below a very simple demonstrative example which work on top of the classList API:
var superToggle = function(element, class0, class1) {
element.classList.toggle(class0);
element.classList.toggle(class1);
}
And you call it in this way:
superToggle(group,'left-to-right', 'right-to-left');
For anyone looking for a short answer, you can do this on one line using the rest parameter introduced in ES6/ES2015:
const toggleCSSclasses = (el, ...cls) => cls.map(cl => el.classList.toggle(cl))
This is pretty close to #attacomsian's answer, but taking advantage of the fact that the rest parameter will return an array - no matter how many arguments is being passed to the function. Which means we can skip the part where we detect whether we're working with a string or an array.
const toggleCSSclasses = (el, ...cls) => cls.map(cl => el.classList.toggle(cl));
const one = document.querySelector(".one");
one.addEventListener("click", () => {
toggleCSSclasses(one, "class1");
});
const two = document.querySelector(".two");
two.addEventListener("click", () => {
toggleCSSclasses(two, "class1", "class2");
});
.class1 { text-decoration: underline }
.class2 { background: steelblue }
<p class="one">Click to toggle one class</p>
<p class="two">Click to toggle two classes</p>
just use the map array.
like
['left-to-right', 'right-to-left'].map(v=> group.classList.toggle(v) )
Here is ES6 version of solution
const classToggle = (el, ...args) => args.map(e => el.classList.toggle(e))
const classToggle = (el, ...args) => {
args.map(e => el.classList.toggle(e))
}
.a {
color: red
}
.b {
background: black
}
.c {
border-color: yellow
}
<button onclick="classToggle(this,'a', 'c','b')" class="a b c ">Click me</button>
And here's old JS code:
var classToggle = function classToggle(el) {
for (
var _len = arguments.length,
args = new Array(_len > 1 ? _len - 1 : 0),
_key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
args.map(function (e) {
return el.classList.toggle(e);
});
};
Answer from year 2020 here!
Found this article helpful from 4/2021
Can use comma separated list of classes like this:
const button = document.getElementById('button')
button.classList.add('btn', 'btn-primary', 'btn-primary--footer')
button.classList.remove('btn', 'btn-primary', 'btn-primary--footer')
or even spread syntax from a list of classes:
const button = document.getElementById('button')
const classes = ['btn', 'btn-primary', 'btn-primary--footer']
button.classList.add(...classes)
button.classList.remove(...classes)
You can extend the DOMTokenList object with the following multiToggle
if (window["DOMTokenList"]) //check if DOMTokenList is an existing object.
{
//multitoggle
DOMTokenList.prototype.multiToggle = function()
{
if (arguments.length > 0) // there needs to be at least one object
{
for (argument in arguments) //loop all arguments
{
var argument = arguments[argument];
//All primitives are allowed as input (Symbol not included). If not a primitive, raise error.
if (Object.prototype.toString.call(argument) !== "[object Undefined]" && Object.prototype.toString.call(argument) !== "[object Null]" && Object.prototype.toString.call(argument) !== "[object String]" && Object.prototype.toString.call(argument) !== "[object Number]" && Object.prototype.toString.call(argument) !== "[object Boolean]")
{
throw new SyntaxError;
}
else
{
if (this.contains(argument)) //check if classList contains the argument.
{
this.remove(argument); //if so remove
}
else
{
this.add(argument); //if not add
}
}
}
}
else
{
throw new Error("The argument is not optional");
}
return undefined; //return undefined as with add and remove.
}
}
multiToggle does not have the force ability of the original toggle. It just turns class names on and off for as many arguments as supplied.
Warning, expanding fixed Objects can cause troubles in the future . When an object gets deprecated or changed your functionality could break, requiring to more maintenance.
There is no direct way but you can create a helper function:
const toggleClass = (el, cls) => {
if (Array.isArray(cls)) {
cls.map((cl) => {
el.classList.toggle(cl);
});
} else {
el.classList.toggle(cls);
}
};
Now just call toggleClass() like below:
// single class
toggleClass(document.querySelector('body'), 'left-to-right');
//multiple classes
toggleClass(document.querySelector('body'), ['left-to-right', 'right-to-left']);
If I need to toggle multiple classes I just create an array and then iterate through it.
var classes = [
"red",
"blue",
"green",
"purple"
]
for (var i = 0; i < classes.length; i++){
p.classList.toggle(classes[i])
}
Assuming that myElement is a valid DOM Element, this works:
['right-to-left', 'left-to-right'].forEach(function(className){
this.classList.toggle(className);
}, myElement);
This Worked for me
let superToggle = (element, class0, class1) => {
element.classList.toggle(class0);
element.classList.toggle(class1);
};
const toggleClasses = (e, classes) => {
classes.forEach((className) => {
e.classList.toggle(className)
});
}
const classes = [
'hidden',
'bg-white',
]
toggleClasses(element, classes)
The following should work; granted that these class-names are defined in your CSS and some elements on the current page have these classNames:
var toggleDirection = function()
{
var ltr, rtl, lst, cls;
ltr = 'left-to-right';
rtl = 'right-to-left';
lst = [].slice.call(document.getElementsByClassName(ltr));
lst = ((lst.length > 0) ? lst : [].slice.call(document.getElementsByClassName(rtl)));
lst.forEach
(
function(node)
{
cls = node.getAttribute('class');
if (cls.indexOf(ltr) > -1)
{ cls.split(ltr).join(rtl); }
else
{ cls.split(rtl).join(ltr); }
node.setAttribute('class', cls);
}
);
}

JavaScript alternatives to handling switch cases that have properties

I have HTML elements defined like this
<div id="Jacob" validation="required alpha-numeric"></div>
<div id="Peter" validation="required minlen:4 maxlen:20"></div>
And in Javascript I was parsing and handling the validation="property1 property2 ... propertyN" like this:
// called from a foreach that uses split(' ')
validate(type) {
switch(type) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
}
}
I realise using a switch like this might be a bit verbose and archaic.
What would be an elegant way to parse parameters that have their own properties/values?
I don't know where you got the impression that switch statements are archaic. They're not.
As far as your particular case, you can use .split(":") to split the individual parts apart, and then match on that:
function validate(type) {
var parts = (type || "").split(":");
switch(parts[0].toLowerCase()) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
case "minlen":
// validate against parts[1]
break;
}
}
If you wanted to use a lookup rather than a switch, you can do that, but I'd say that's just a matter of preference:
var validators = {
"required": function (value) {
},
"alpha-numeric": function (value) {
},
"minlen": function (value, len) {
}
};
function validate (value, type) {
var parts = (type || "").split(":");
var validator = validators[parts[0].toLowerCase()];
if (validator) {
var result = validator(value, parts[1]);
}
}
One potential benefit of the second approach is that new validators can be added to the validator object at runtime, though if you wanted to make a pluggable system, you'd probably want to go with something more robust than just a plain object that can be arbitrarily modified.
you can create json of key function pair. And pick function using your type and call it.
var objOfFunction = {};
objOfFunction["required"] = requiredFunction;
objOfFunction["alpha-numeric"] = alphanFunction;
function validate(type) {
objOfFunction[type]();
}
function requiredFunction(){
alert("required");
}
function alphanFunction(){
alert("in alpha");
}
validate("required");
validate("alpha-numeric");

re-write ifelse to switch

How would I go about re-writing this ifelse to a switch?
var d = document,
a = 'foo',
b = 'bar',
c = 'foobar';
if (d.URL.indexOf(a) != -1 || d.URL.indexOf(b) != -1)
{
// do this
}
elseif(d.URL.indexOf(c) != -1)
{
// do something else
}
it goes on for about 10 more ifelse which is why i'd prefer to change it to switch
I've searched for answers and found nothing so tried this:
function io(i)
{
d.URL.indexOf(i) != -1;
}
switch (io())
{
case a:
case b:
// do this
break;
case c:
// do this
break;
}
and a few variations of the same thing but I'm very much a JS novice and so knew I was probably wrong (which I was).
I think the best thing to do here would be to actually create an array of objects with their functions, then you would just cycle through that array using every. Here's some demonstration code using your above example:
var choices = [
{
value: foo,
fn: function () { // do this }
},
{
value: 'foobar',
fn: function () { // do something else }
}
];
choices.every( function( choice ) {
if ( document.URL.indexOf( choice.value ) !== -1 ) {
choice.fn();
return false;
} else return true;
});
If you have functions you know will be used multiple times, you just create them outside of the array and assign them to the fn key of multiple objects. By using return true when you don't encounter it, it'll keep moving through the array, then when it hits one where it's true, it'll return false and end the every function.

Categories

Resources