Extremely annoying JavaScript array/object error - javascript

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!

Related

ES6 calling method in another class, using modules

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.

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Remove element by value a-la Knockout

This is something of a two-part question that has to do with manipulating elements within an array of data in Angular. It seems like pretty universally the way to remove an element from an array in the ViewModel is
$scope.array.splice(index, 1);
This seems a little shaky to me, and I prefer how Knockout handles this with .remove and observable arrays: vm.array.remove(item).
I have found that you can do this which is a bit better:
$scope.array.splice($scope.array.indexOf(item), 1);
but it's more verbose and .indexOf may not work as you expect depending upon what item is.
Is there any construct for Angular that will allow you to easily remove an item from an array by its value?
Also based on this video from Egghead.io, it makes sense to remove dependencies within ViewModel methods and not rely on scope. Would it be preferred to pass in the array that you were removing the item from as well:
<input type=submit ng-click="remove(array, item)">
array.splice(array.indexOf(item), 1)
Or is there a reason to prefer using $scope (or the controller) within the remove method?
Unfortunately or Fortunately, Knockout does it the same way we are doing with Angular i.e. splice method
If you look at the source code of observableArray.remove(item) in knockout library -
'remove': function (valueOrPredicate) {
var underlyingArray = this.peek();
var removedValues = [];
var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
for (var i = 0; i < underlyingArray.length; i++) {
var value = underlyingArray[i];
if (predicate(value)) {
if (removedValues.length === 0) {
this.valueWillMutate();
}
removedValues.push(value);
underlyingArray.splice(i, 1);
i--;
}
}
if (removedValues.length) {
this.valueHasMutated();
}
return removedValues;
}
It does the same thing, it parse through the array and compare the given value and performs splice.
They have written reusable module for the same to make it easy to use for developers. I believe you can do the same by writing custom directive in your Angular code. You can use above code for a reference. It's just that Angular does not have any reusable directive for that... yet.. may be we can ask for a pull request after making one :-)
But your question is very good and one should have such reusable module.

Javascript - Array of prototype functions

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
}

Building Classes in javascript for beginner

I'm well versed with javascript and jQuery. I've just never built a class before. Maybe a class is not even what I'm looking for.
I have a function I call to launch an overlay and it's used a lot and contains some parameters.
function launchOverlay(method, content, width, closeBtn) {
$("body").append('<div id="overlay-backdrop" style="display:none"></div>');
$("#overlay-backdrop").css({
width: $(document).width(),
height:$(document).height()
}).fadeIn();
$("#overlay-backdrop").append('<div id="overlay-canvas-area"><div class="inner-canvas-area"></div><div>');
if(typeof closeBtn == 'undefined'){
$("#overlay-canvas-area").append('<div class="close-btn"><a class="close" onClick="closeOverlay()">Close</a></div>');
}
if (method == "load"){
$("#overlay-canvas-area .inner-canvas-area").load(content);
}if(method == "append"){
$("#overlay-canvas-area .inner-canvas-area").append(content);
}
var canvasAreaWidth = width+($("#overlay-canvas-area").width());
var canvasAreaHeight = $("#overlay-canvas-area").height();
$("#overlay-canvas-area").animate({
top:((($(document).height())-(canvasAreaHeight))/2),
left: ((($(document).width())-(canvasAreaWidth))/2)
},700);
}
I find myself modifying this constantly to fit my needs and then going back to old instances and modifying the function call. I would also like to pass json as settings with the function.
first question is, are classes what I'm looking for?
if so, where is a good place to learn?
if not, what should I do to improve functionality?
A class (or in javascript a "prototype" is actually the more correct term) is appropriate when there is a lasting object that contains some data and then you want to operate on that data with multiple different methods over time.
The class allows you to neatly specify how the data is stored and what methods operate on the data.
If you just have one operation that produces an output and can take in a variety of different input data, then you won't really benefit from a class. You just need a function that takes a variety of parameters and chooses its operation based on what was passed to it.
In javascript when there are lots of options for a function and they may be variable, then it is sometimes common to pass in an options object that contains properties that direct the operation of the function. The function can then examine which properties are present and what values they have to select how it should behave. The use of the options object can allow much simpler maintenance any time you want to add or modify a parameter rather than continuing to add more and more function arguments. An options object like this can also be passed around more easily rather than passing every single argument individually. You can also create a default state for the options object that contains all the default values for the arguments (what their value should be if they aren't passed). While all of this can be done with multiple traditional function arguments, it can be a lot cleaner to code with an options object.
The use of an options object would look like this:
function doWhatever(mainData, options) {
if (options.foo) {
// do it one way
} else {
// do it the other way
}
}
doWhatever(myData, {foo: true, output: "commas", fee: "whatever"};
For a straight class like implementation I like this: http://ejohn.org/blog/simple-class-instantiation/
But it sounds like you want to create something that in jQuery parlance would be called a 'widget', which gives you a lot of what you are asking for free, plus more:
http://ajpiano.com/widgetfactory/#slide1
http://wiki.jqueryui.com/w/page/12138135/Widget%20factory
http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
I agree with the others because this function is entirely behavior with no state. You might consider putting the function into a namespace along with related UI helper functions, but that's strictly for better organization and ease of use on multiple pages.
Also, you could improve the readability and performance of this function by storing the jQuery objects returned by append and reusing them for further calls.
function launchOverlay(method, content, width, closeBtn) {
var $overlay = $("body").append('<div id="overlay-backdrop" style="display:none"></div>');
$overlay.css({
width: $(document).width(),
height:$(document).height()
}).fadeIn();
var $canvas = $overlay.append('<div id="overlay-canvas-area"><div class="inner-canvas-area"></div><div>');
if (typeof closeBtn == 'undefined') {
$canvas.append('<div class="close-btn"><a class="close" onClick="closeOverlay()">Close</a></div>');
}
if (method == "load") {
$("#overlay-canvas-area .inner-canvas-area").load(content);
}
else if (method == "append") {
$("#overlay-canvas-area .inner-canvas-area").append(content);
}
var canvasAreaWidth = width + $canvas.width();
var canvasAreaHeight = $canvas.height();
$canvas.animate({
top: (($(document).height() - canvasAreaHeight) / 2),
left: (($(document).width() - canvasAreaWidth) / 2)
}, 700);
}
Note that I've left the var declarations inline to match your style, but you should be aware that their declarations are hoisted to the top of the function. Here it doesn't matter, but it could bite you in more complicated functions later.

Categories

Resources