I know there is many questions like this asked, but I have been searching for hours and can't find any answers. I have this method, which takes in a parameter, which should be ID of two selects. Using this parameter, I want to determine which select is used and execute the if statement, but to no avail. When I run it, it shows no errors in console in Chrome and it does nothing. Can anyone shed some light on it, this is the method in one export class:
static styleCircle(select) {
if(this.select === ELEMENTS.ELEMENT_COLOR_SELECT) {
var getColor = ELEMENTS.ELEMENT_COLOR_SELECT;
var colorValue = getColor.options[getColor.selectedIndex].value;
ELEMENTS.ELEMENT_STYLE_CIRCLE.style.backgroundColor = colorValue;
} else if(select == ELEMENTS.ELEMENT_BORDER_SELECT) {
var getRadius = ELEMENTS.ELEMENT_BORDER_SELECT;
var radiusValue = getRadius.options[getRadius.selectedIndex].value;
ELEMENTS.ELEMENT_STYLE_CIRCLE.style.borderRadius = radiusValue;
}
}
This is it being called in another class, on two select elements, and the class is imported at the top of the file:
ELEMENTS.ELEMENT_COLOR_SELECT.onchange = Script.styleCircle(this);
ELEMENTS.ELEMENT_BORDER_SELECT.onchange = Script.styleCircle(this);
ELEMENTS is a file with constants, which are just being used to get ID's from the HTML file. I used other methods like this, with onclick events, but none had parameters, and now I'm stuck here. Thanks in advance.
You don't want to call the functions right now but instead you probably want to pass functions. Through that you can access the proper this and pass it to styleCircle:
ELEMENTS.ELEMENT_COLOR_SELECT.onchange = function() {
Script.styleCircle(this);
};
ELEMENTS.ELEMENT_BORDER_SELECT.onchange = function() {
Script.styleCircle(this);
};
Additionally this.select is probably causing you troubles as window.select is undefined.
First step would be to try debugging and ensure select is equivalent to either of those constants. Make sure you have full branching coverage in your debugging. That would mean start by adding an else statement to that if/else if statement - it's possible that your select is not equal to either constant and so neither branch is run.
Related
I want to make parts of a Cypress test suite run a set of functions based on a configuration variable. I.e. version 1 of the function showMessage should be used if the version variable is 1, and version 2 if the version variable is 2. I can do it with if statements like demonstrated below. I am a JavaScript noob, but I know there are more elegant ways of doing it.
function showMessage(version) {
if (version == '1') {
console.log('Version 1');
}
else {
console.log('Version 2')
}
}
showMessage('2');
I would ideally like to have the functions in separate folders, each defining their own version of showMessage (without the if statement), but I am not sure how to do it properly in JavaScript.
In a way, I'm asking for an extremely lightweight dependency injection mechanism.
There is many many ways to do that. It is basically a decision of what is less work and is the most readable. But at the end there is never a way around for selecting what you want.
A different approach to if in each function would be:
var a = {};
var b = {};
a.showMessage = () => { console.log('Version 1'); }
b.showMessage = () => { console.log('Version 2'); }
version ='1'
//set up 'reference' object c in one place, based on the selected version
c = (version == '1' ? a : b);
//Then every time we call showMessage, use the reference object (no ifs)
c.showMessage();
Another option would be to use the prototype of a function. That is to be said, literally the same as the above example but with the fact that it is more reuseable in some kind of way.
I'm only sharing a small bit of code because there is so much going on, and I hope this is enough to answer my question.
I have some existing JS where a value is determined with an OR statement and I think I need to convert that to an IF statement. The final output is currently giving me both values if they both exist, and I only want "question" where both "question" and "name" values exist.
var question = new fq.Question(questionData.answerId, topicId,
questionData['question'] || questionData['name'],
questionData['text']);
Instead of using the OR operator (answerData['question'] || answerData['name']), I'd like to do something similar to the following:
if (questionData['question'] is undefined) {
use questionData['question'];
} else {
use instead questionData['name']
}
But, I don't know how I might accomplish such a statement within the () in the existing code pasted above. The name variable/value is always present, so there's no risk in defaulting to that. Question is only defined some of the time. And I don't ever want both appearing.
This is probably outside of the scope of my query here, but to fill in a little more detail, this code eventually outputs JSON files for topics and questions. Topics only have names values, and questions have both names and questions, but I only want the questions json to include questions values, not names. I'm pretty sure this is the key part in all of the JS to determin
Create a function and get value from there.
Need to remember scope of function:
Example Snippet:
var that = this;
var question = new fq.Question(questionData.answerId, topicId,
that.getValue(),
questionData['text']);
function getValue() {
if (questionData['question']) { //null and undefined both are false
return questionData['question']
} else {
return questionData['name']
}
}
I'm a javascript newbie so I'm writing ugly code so far sometimes due to my lack of experience and how different it is to the languages I'm used to, so the code I'll post below works, but I'm wondering if I'm doing it the right way or perhaps it works but it's a horrible practice or there is a better way.
Basically, I have a little dude that moves within a grid, he receives from the server an action, he can move in 8 directions (int): 0:up, 1: up-right, 2: right... 7: up-left.
the server will send him this 0 <= action <= 7 value, and he has to take the correct action... now, instead of using a switch-case structure. I created a function goUp(), goLeft(), etc, and loaded them in an array, so I have a method like this:
var getActionFunction = actions[action];
actionFunction();
However, what to set all this up is this:
1) create a constructor function:
function LittleDude(container) {
this.element = container; //I will move a div around, i just save it in field here.
}
LittleDude.prototype.goUp() {
//do go up
this.element.animate(etc...);
}
LittleDude.prototype.actions = [LittleDude.prototype.goUp, LittleDude.prototype.goUpLeft, ...];
//In this array I can't use "this.goUp", because this points to the window object, as expected
LittleDude.prototype.doAction = function(action) {
var actionFunction = this.actions[action];
actionFunction(); //LOOK AT THIS LINE
}
Now if you pay attention, the last line won't work.. because: when i use the index to access the array, it returns a LittleDude.prototype.goUp for instance... so the "this" keyword is undefined..
goUp has a statement "this.element"... but "this" is not defined, so I have to write it like this:
actionFunction.call(this);
so my doAction will look like this:
LittleDude.prototype.doAction = function(action) {
var actionFunction = this.actions[action];
actionFunction.call(this); //NOW IT WORKS
}
I need to know if this is hackish or if I'm violating some sort of "DO NOT DO THIS" rule. or perhaps it can be written in a better way. Since it seems to me kind of weird to add it to the prototype but then treating it like a function that stands on its own.
What you are trying to do is one of the possible ways, but it is possible to make it more simple. Since object property names are not necessary strings, you can use action index directly on prototype. You even don't need doAction function.
LittleDude = function LittleDude(container) {
this.container = container;
}
LittleDude.prototype[0] = LittleDude.prototype.goUp = function goUp() {
console.log('goUp', this.container);
}
LittleDude.prototype[1] = LittleDude.prototype.goUpRight = function goUpRight() {
console.log('goUpRight', this.container);
}
var littleDude = new LittleDude(123),
action = 1;
littleDude[action](); // --> goUpRight 123
littleDude.goUp(); // --> goUp 123
actionFunction.call(this); //NOW IT WORKS
I need to know if this is hackish or if I'm violating some sort of "DO NOT DO THIS" rule. or perhaps it can be written in a better way.
No, using .call() is perfectly fine for binding the this keyword - that's what it's made for.
Since it seems to me kind of weird to add it to the prototype but then treating it like a function that stands on its own.
You don't have to define them on the prototype if you don't use them directly :-) Yet, if you do you might not store the functions themselves in the array, but the method names and then call them with bracket notation:
// or make that a local variable somewhere?
LittleDude.prototype.actions = ["goUp", "goUpLeft", …];
LittleDude.prototype.doAction = function(action) {
var methodName = this.actions[action];
this[methodName](); // calls the function in expected context as well
}
i'm implementing a charcounter in the UI, so a user can see how many characters are left for input.
To count, i use this simple function:
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = source.value.length.toString();
}
source contains the field which values we want to meassure
layerID contains the element ID of the object we want to put the result in (a span or div)
outPanel is just a temporary var
If i activate this function, while typing the machine really slows down and i can see that FF is using one core at 100%. you can't write fluently because it hangs after each block of few letters.
The problem, it seems, may be the value.length() function call in the second line?
Regards
I can't tell you why it's that slow, there's just not enough code in your example to determine that. If you want to count characters in a textarea and limit input to n characters, check this jsfiddle. It's fast enough to type without obstruction.
It could be having problems with outPanel. Every time you call that function, it will look up that DOM node. If you are targeting the same DOM node, that's very expensive for the browser if it's doing that every single time you type a character.
Also, this is too verbose:
source.value.length.toString();
This is sufficient:
source.value.length;
JavaScript is dynamic. It doesn't need the conversion to a string.
I doubt your problem is with the use of innerHTML or getElementById().
I would try to isolate the problem by removing parts of the function and seeing how the cpu is used. For instance, try it all these ways:
var len;
function typerCount(source, layerID)
{
len = source.value.length;
}
function typerCount(source, layerID)
{
len = source.value.length.toString();
}
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = "test";
}
As artyom.stv mentioned in the comments, cache the result of your GetElementByID call. Also, as a side note, what is GetElementByID doing? Is it doing anything else other than calling document.getElementById?
How would you cache this you say?
var outPanelsById = {};
function getOutPanelById(id) {
var panel = outPanelsById[id];
if (!panel) {
panel = document.getElementById(id);
outPanelsById[id] = panel;
}
return panel;
};
function typerCount(source, layerId) {
var panel = getOutPanelById(layerId);
panel.innerHTML = source.value.length.toString();
};
I'm thinking there has to be something else going on though, as even getElementById calls are extremely fast in FF.
Also, what is "source"? Is it a DOMElement? Or is it something else?
Basically, I am rewriting part of one of my web applications. I had a script that would collapse some or all panels of the interface at once, and another to code them.
However, my old functions looked really ugly, and were annoying to type and not powerful enough:
function collapse_all()
{
document.getElementById("panel_1").style.display="none"
document.getElementById("panel_2").style.display="none"
document.getElementById("panel_3").style.display="none"
}
function expand_all()
{
document.getElementById("panel_1").style.display=""
document.getElementById("panel_2").style.display=""
document.getElementById("panel_3").style.display=""
}
Now I have this:
function panel() //first variable in argument is collapse or expand, all others are panels to act on
{
var panels = panel.arguments
alert(typeof panel.arguments)
var mode = panels.shift() //here's my problem
if(mode=="collapse") {mode="none"}
if(mode=="expand") {mode=""}
var items = panels.length
for (i = 0;i < items;i++) {document.getElementById(panels[i]).style.display=mode}
}
panel("collapse","panel_1","panel_2","panel_3")
I have a problem though. Firebug tells me panels.shift() is not a function. With some Googling I managed to find out that panel.arguments isn't an array but an object, so I can't use array methods on it. I'm just really confused as to how I could either convert the object into an array or find another workaround, as I know next to nothing about JavaScript objects. Some example code would be highly appreciated.
You can convert the arguments object into an array like this:
var argsArray = Array.prototype.slice.call(arguments);
What this does is use the slice method common to all arrays via Array.prototype to create a genuine Array object from the array-like arguments. call() (a method of all functions) is used to call this slice method with a this value of arguments and no parameters, which has the effect of copying all of the elements of this into a new array. This may seem devious or hacky but it is actually designed into the language: see the note at the bottom of section 15.4.4.10 of the ECMAScript 3rd Edition spec.
Also, within a function you are provided the arguments object as a variable, so you don't need to access it as a property of the function object as you are doing. In your case, just use arguments rather than panel.arguments.
You could keep it much simpler (cleaned up your formatting, semi-colons, etc.):
function panel()
{
var panels = Array.prototype.slice.call(arguments);
var displayMode = (panels[0] == "collapse" ? "none" : "");
for (var i = 1; i < panels.length - 1; i++)
{
document.getElementById(panels[i]).style.display = displayMode;
}
}
Also, if you're rewriting your application, it might be a good time to consider using things like jQuery. You could assign each one of your panels a certain class name, and reduce your code to something like this:
function panel(hide)
{
$('.className').css({ display: (hide ? 'none' : '') });
}
which you could use like so:
panel(true); // or
panel(false);
Or, because now it's so syntactically simple, you might as well just create two separate functions so that your code is straightforward and you know exactly what it's going to do from the function names alone:
function showPanels() {
$('.className').css({ display: '' });
}
function hidePanels() {
$('.className').css({ display: 'none' });
}
And finally, if you don't worry about doing it via CSS, you could really shorten your script to this, which can't be any clearer:
function showPanels() {
$('.className').show();
}
function hidePanels() {
$('.className').hide();
}
Cheers!