I am trying to access to a simple nested Array, doing this:
var currMenu = 1;
while ( currMenu < menu.length ) {
alert(currMenu);
alert(menu[0][currMenu].text);
currMenu++;
}
Despite alerts are throwing the correct values, I am getting this error on firebug: TypeError: menu[0][currMenu] is undefined.
What is happening?
Thanks!
Edit: Sorry, I was rushing, here you have the "menu" structure:
menu[0] = new Array();
menu[0][0] = new Menu(false, '', 15, 50, 20, '','' , 'navlink', 'navlink');
menu[0][1] = new Item('someText', '#', '', 100, 10, 1);
And the object Item:
function Item(text, href, frame, length, spacing, target) {
this.text = text;
if (href == '#') {
this.href = '#';
} else if (href.indexOf('http') == 0) {
this.href = href;
} else this.href = href;
this.frame = frame;
this.length = length;
this.spacing = spacing;
this.target = target;
// Reference to the object's style properties (set later).
this.ref = null;
this.showLoadingBar = false;
}
Assuming your menu is coherent with the [0][currMenu], you should access it like this :
while ( currMenu < menu[0].length ) {
alert(currMenu);
alert(menu[0][currMenu].text);
You're looking at the length of the "menu" array, but you're accessing the array at the zero-th index of that array (which may or may not be an array; I can't tell from the code you've posted).
Related
First of all I'm not a programmer. I need to use some really basic HTML, CSS and XML for my work. The program I am using allows running javascripts, too.
I usually have a lot of variables from my XML files. They go by something like this:
VAL001
VAL002
VAL003
VAL004
You get it.
These variables are often checkboxes. The values can be either 'Checked' or 'Unchecked'.
Instead of embedding these variables in the HTML code, I tend to convert it to something else so it gets nicer. Like this:
if ( VAL001 == 'Checked' ) CHK001 = '✓';
else CHK001 = '';
When this is done, I insert CHK001 (instead of VAL001) in the HTML so I get a nice check mark if the box was checked and nothing when it was not checked. When there are a lot of these boxes it's not too effective to do it one by one.
What I tried in JavaScript is:
var i;
for ( i = 1, i <= 9, i++ ) {
if ( VAL00$i == 'Checked' ) CHK00$i = '✓'
else CHK00$i = '';
}
var j;
for ( j = 10, j <= 99, j++ ) {
if ( VAL0$j == 'Checked' ) CHK0$j = '✓'
else CHK0$j = '';
}
I thought that this would replace the last digits with i and j and I would get what I need. Unfortunately this just brings up a ReferenceError saying that VAL00$i can't be found.
If I replace the $i and $j with [i] and [j] I get the same ReferenceError but this time i and j are not told to be wrong so I get that VAL00 can't be found. A simple solution would really speed up things for me. Thank you in advance!
You could put your variables in an array and use map to check and change the variables to be a tick or not.
var array = [
VAL001,
VAL002,
VAL003,
VAL004
];
var newArray = array.map(val=>{
if (val === 'Checked') return '✓';
else return '';
});
Alteratively, if you need to know the names of the variables after checking everything you could use an object.
var obj = {
VAL001: VAL001,
VAL002: VAL002,
VAL003: VAL003,
VAL004: VAL004
};
var newObj;
for (var i of Object.keys(obj){
if (obj[i] === 'Checked') newObj[i] = '✓';
else newObj[i] = '';
}
If you are having VAL001 variables as property in obj then you can perform like below.
Here i.toString().padStart(3, 0), for i = 1 it will return 001 similarly for i=10 it will return 010; You can access property of object with obj[propertyName]. So these values will be accessible with obj[VAL${index}].
var obj = {
VAL001: 'Checked',
VAL002: '',
VAL003: 'Checked',
VAL004: '',
VAL010: '',
VAL099: 'Checked',
};
var result = {};
for (var i = 1; i <= 99; i++) {
let index = i.toString().padStart(3, 0);
if (obj.hasOwnProperty(`VAL${index}`)) {
if (obj[`VAL${index}`] == 'Checked') result[`CHK${index}`] = '✓'
else result[`CHK${index}`] = '';
}
}
console.log(result);
If you are having variables in global scope then you can use windows["VAL001"].
var VAL001 = 'Checked',
VAL002 = '',
VAL003 = 'Checked',
VAL004 = '',
VAL010 = '',
VAL099 = 'Checked';
for (var i = 1; i <= 99; i++) {
let index = i.toString().padStart(3, 0);
if (window.hasOwnProperty(`VAL${index}`)) {
if (window[`VAL${index}`] == 'Checked') window[`CHK${index}`] = '✓'
else window[`CHK${index}`] = '';
console.log(`CHK${index} = ` + window[`CHK${index}`]);
}
}
We are lacking some information about your environment, but assuming your framework gives you a set of global variables, this should get you started:
for (var i=1, i<=99, i++) {
var i_padded = i.toString().padStart(3, 0);
if (window["VAL" + i_padded] == 'Checked') {
window["CHK" + i_padded] = '✓';
} else {
window["CHK" + i_padded] = "";
}
}
In order to access your global variables I am using the window object here. This is assuming you are running this JS in a browser or browser-like environment.
I have this object:
var registered_screens = {
handle_1 : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3'
};
And here's what I tried to get its neighbors:
function get_neighbors( current_step ) {
var steps = {
next: '',
previous: ''
};
for( var i in registered_screens ) {
if( current_step == registered_screens[i] ) {
if(registered_screens.hasOwnProperty(registered_screens[i])) {
steps.next = registered_screens[i+1];
}
if(registered_screens.hasOwnProperty(registered_screens[i-1])) {
steps.previous = registered_screens[i-1];
}
}
}
return steps;
}
Obviously, this is a no-go because an object can't be parsed the same as an array, but just wanted to show what I tried.
What I'd want to get is, if I call get_neighbors('handle_2'), return:
steps { prev : 'handle_1' , next : 'handle_3' }
Or, for get_neighbors('handle_3'):
steps { prev : 'handle_2', next : null }
I've also attempted:
var registered_screens = {
handle_one : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3'
};
var key_values = Object.keys(registered_screens);
for( var i = 0; i < key_values.length; i++ ) {
console.log(registered_screens.key_values[i]);
}
But this throws:
main.js?ver=4.9.6:18 Uncaught TypeError: Cannot read property '0' of undefined
main.js?ver=4.9.6:18
main.js?ver=4.9.6:270
Funnily enough, I checked what the values for key_values[i] are and they're the right handles.
It seems JS has a hard time building variables out of strings?
I don't like it much and would look at a restructure to make registered_screens an array. I also would not trust this code due to object order can not be guaranteed.
That said, this will work with my browser.
Edit: Added an array version, which I would trust but would expand for blank (undefined) results.
// array version
var aScreens = [];
aScreens['handle_one'] = 'step_1';
aScreens['handle_2'] = 'step_2';
aScreens['handle_3'] = 'step_3';
function getArrayPrevNext(a,current) {
var x,p = '',n = '',found = false;
for(x in a) {
if (found) {
n = x;
break;
} else if (x == current) {
found = true;
} else {
p = x;
}
}
return {prev:p,next:n};
}
var aSteps = getArrayPrevNext(aScreens,'handle_3');
console.log('array prev['+ aSteps.prev +'], next['+ aSteps.next +']');
var p = aSteps.prev, n = aSteps.next;
console.log('handle prev['+ aScreens[p] +'], next['+ aScreens[n] +']');
console.log('handle alt prev['+ aScreens[aSteps.prev] +'], next['+ aScreens[aSteps.next] +']');
// Object version
var registered_screens = {
handle_one : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3'
};
function getPreviousNext(obj,current) {
var prev = '', nxt = '', found = false;
Object.keys(obj).forEach(function(key) {
if (! nxt) {
if (found) {
nxt = key;
} else if (key == current) {
found = true;
} else {
prev = key;
}
}
});
return {prev:prev,next:nxt};
}
var steps = getPreviousNext(registered_screens,'handle_3');
console.log('Object steps:['+ steps.prev +']['+ steps.next +']');
Your second attempt is what I thought as well, it should work fine if you correct the key access like below
var registered_screens = {
handle_one : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3'
};
var key_values = Object.keys(registered_screens);
for( var i = 0; i < key_values.length; i++ ) {
console.log(registered_screens[key_values[i]]);
}
As far as I know, the keys in the object are un-ordered and we should not rely on that order, like others have mentioned, it's order will not be the same as when you created, but your requirement seems like it can make use of Object.keys to iterate and find next and prev keys to some extent
To your question, why this registered_screens[key_values[i]] works and not registered_screens.key_values[i], the dot notation will not work for dynamic keys, i.e key_values[i] is not a key, it's a variable holding the key, in such cases you have to access it like an array like Object[keyNameHolder]
This script does a very basic parsing of object keys - it assumes it is always in the format of handle_{n} - based on that it creates an array that holds the keys in proper order, which then is searched for and uses n-1 and n+1 to return prev and next (if possible, else null). And yes i know, most browsers would sort it correctly nonetheless so that in most scenarios you would get the proper order (included a console output for comparison)
var screens = {
handle_1 : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3',
handle_4 : 'step_4',
handle_5 : 'step_5',
handle_6 : 'step_6',
handle_7 : 'step_7',
handle_8 : 'step_8',
handle_9 : 'step_9',
handle_10 : 'step_10',
handle_11 : 'step_11',
},
keyParser = (key) => parseInt(key.replace('handle_', '')),
keySorter = (a, b) => keyParser(a) - keyParser(b),
handleKeys = Object.keys(screens).sort(keySorter);
// Compare key ordering in your browser
// It will be most likely identic, since most modern browsers understand that
// it should sort by {str}_{int} and not by {str}_{str} if an {int} is present
// but chances are that for instance IE9 would do the latter, so it could be that
// with browser ordering handle_10 and handle_11 come after handle_1
console.log('browser ordering:', Object.keys(screens));
console.log('parsed ordering:', handleKeys);
function getSteps(handle) {
var pos = handleKeys.indexOf(handle);
if(pos === -1) throw(`Can't find handle ${handle} in screens`);
return {
current: screens[handleKeys[pos]],
prev: pos > 0 ? screens[handleKeys[pos-1]] : null,
next: pos < handleKeys.length-1 ? screens[handleKeys[pos+1]] : null
}
}
console.log(
getSteps('handle_1'),
getSteps('handle_2'),
getSteps('handle_6'),
getSteps('handle_10'),
getSteps('handle_11')
);
Also a good read: https://hackernoon.com/out-of-order-keys-in-es6-objects-d5cede7dc92e
As far as I know the answer is there is no direct way, but you can play around this in many ways.
One idea is that you can play with the naming convention of your data, for example,
call the object items as handle_1 instead of "handle_one" and so on, that
way you can loop around the array using index ['handle_' + i] but notice that you can't do ['handle_' + i + 1] or else you will have a wrong index value because the string conversion will happen before the summation.
I hope this helps.
I made this work:
var registered_screens = {
handle_1 : 'step_1',
handle_2 : 'step_2',
handle_3 : 'step_3'
};
function get_neighbors( current_step ) {
var steps = {
next: '',
previous: ''
};
var key_values = Object.keys(registered_screens);
for( var i = 0; i < key_values.length; i++ ) {
if( current_step == registered_screens[key_values[i]]) {
if( !(registered_screens[key_values[i-1]] == null) ) {
steps.previous = registered_screens[key_values[i-1]];
}
if( !(registered_screens[key_values[i+1]] == null) ) {
steps.next = registered_screens[key_values[i+1]];
}
}
}
return steps;
}
And so, get_neighbors('step_2') reliably (in my tests) returns:
steps : { next : 'step_3', previous: 'step_1' };
i have a problem for getting this OOP works. basically, this $("notfound") is not on document. it put an error. but if change it to $("parent") it works because it is on document.
check this fiddle :
https://jsfiddle.net/k6j70f1h/8/
in console.log the child is undefined.
how to get this things works?
what's wrong with my code?
"use strict"
var $, i;
(function() {
$ = function(el, context) {
context = context || document;
return new obj$(el, context);
};
var obj$ = function(el, context) {
var cl = context.getElementsByClassName(el),
loop = cl.length;
this.length = loop;
for (i = 0; i < loop; i++) {
this[i] = cl[i];
}
};
obj$.prototype = {
find : function(el) {
if (this.length == 1) {
return $( el, this[0] );
}
},
css : function(obj, data) {
if (this.length == 1) {
this[0].style[obj] = data;
return this;
}
}
};
})();
var parent = $("notfound"), // this one cause error
child = parent.find("child"); // throw an error child is undefined
child.css("color", "orange");
parent.css("color", "purple");
<div class="parent">parent
<div class="child">child</div>
</div>
The line you've said is causing the error is not causing the error.
The line causing the error is:
child.css("color", "orange");
It's causing the error because this line:
var parent = $("notfound"),
...returns a parent object with length == 0, and so this line:
child = parent.find("child"); // throw an error child is undefined
...calls find on an object where this.length is not 1, so the code in find doesn't go into the body of your if statement and you don't return anything. That means calling parent.find(...) results in undefined, which you've assigned to child. Thus, child.css(...) is an attempt to call css on undefined.
If you want to make something jQuery-like, you'll want to add
return $();
...find parent.find if this.length is 0 (at a minimum):
find : function(el) {
if (this.length == 1) {
return $( el, this[0] );
}
return $();
}
Similarly, if you want to emulate jQuery, you'll always want to return this from your css function, not just if you have an element.
Here's an update with the minimum necessary changes:
"use strict"
var $, i;
(function() {
$ = function(el, context) {
context = context || document;
return new obj$(el, context);
};
var obj$ = function(el, context) {
var cl = context.getElementsByClassName(el),
loop = cl.length;
this.length = loop;
for (i = 0; i < loop; i++) {
this[i] = cl[i];
}
};
obj$.prototype = {
find : function(el) {
if (this.length == 1) {
return $( el, this[0] );
}
return $(); // Added
},
css : function(obj, data) {
if (this.length == 1) {
this[0].style[obj] = data;
}
return this; // Moved
}
};
})();
var parent = $("notfound"), // this one cause error
child = parent.find("child"); // throw an error child is undefined
child.css("color", "orange");
parent.css("color", "purple");
<div class="parent">parent
<div class="child">child</div>
</div>
There is a little problem with this code:
function getParameters() {
var searchString = document.getElementById('input1').value,
params = searchString.split("&"),
hash = {};
if (searchString == "") return {};
for (var i = 0; i < params.length; i++) {
var val = params[i].split("=");
hash[unescape(val[0])] = unescape(val[1]);
}
console.log(hash);
//return hash;
if(val[0] == "class"){ //alert(val[1]);
$.each(hash, function( attribute, value ) {
test_div.setAttribute(attribute,value);
});
}
else if(val[0] == "color"){ //alert(val[1]);
$.each(hash, function( attribute, value ) {
test_div.style[attribute]=value;
});
}
monitor_test_div.innerText = ccc.innerHTML;
}
Depending by the order in which the parameters are inserted, they are repeated or dont work...
style a div using escaped URL parameters
Demo: JSFiddle 1
Demo: JSFiddle 2
I would like to obtain this:
Example 1:
input:
opacity=0&src=link1&color=red&color=green&src=link2&height=200
output:
<div src="link2" style="color: green;"></div>
Example 2:
input:
src=link1&color=red or color=red&src=link1
output:
<div src="link1" style="color: red;"></div>
in your line
if(val[0] == "class")
you are only checking the first element in your val array.
what you would want to do, is iterate through all the hash objects and simply check the attribute like this:
function getParameters() {
var searchString = document.getElementById('input1').value,
params = searchString.split("&"),
hash = {};
if (searchString == "") return {};
for (var i = 0; i < params.length; i++) {
var val = params[i].split("=");
hash[unescape(val[0])] = unescape(val[1]);
}
console.log(hash);
//return hash;
$.each(hash, function( attribute, value ) {
if(attribute=="color"){
test_div.style[attribute]=value;
}
else if(attribute=="src"){
alert(attribute);
test_div.setAttribute(attribute,value);
}
});
}
here is a working FIDDLE
Maybe you want something like this:
var test_div = $('#test_divs_id');
for (var i = 0; i < params.length; i++) {
var val = params[i].split("=");
var key = unescape(val[0]);
var val = unescape(val[1]);
switch(key) {
case 'class': // treat 'class' key by ...
test_div.addClass(val); // ... adding the value as a class
break;
case 'src': // treat 'src' key,
case 'href': // treat 'href' key, maybe more ...
test_div.attr(key, val); //... by adding as an attribute with value
break;
default: // all other keys...
test_div.css(key, val); // ... are assumed css style names with value
break;
}
EDIT: Extended the switch with the examples + possibly more attributes
I'm trying to write a Javascript function inside a Java GWT code that gets the value of the following styles
"direction", "fontFamily", "fontSize", "fontSizeAdjust", "fontStyle", "fontWeight", "letterSpacing", "lineHeight", "padding", "textAlign", "textDecoration", "textTransform", "wordSpacing"
The getComputedStyle was useful in all browsers except IE8 which doesn't support such function as I understand
I looked at the posts about smiler subject here but all of them failed to get one of the above styles
smiler subject posts 1, 2.
Here is my initial solution without the IE8 special case
public static native String getStyleProperty(Element element, String style) /*-{
if (element.currentStyle) {
return element.currentStyle[style];
} else if (window.getComputedStyle) {
return window.getComputedStyle(element, null).getPropertyValue(
style);
}
}-*/;
Any suggestions for a good getComputedStyle replacement function for IE8 ?
Look over here: http://snipplr.com/view/13523/
The code:
if (!window.getComputedStyle) {
window.getComputedStyle = function(el, pseudo) {
this.el = el;
this.getPropertyValue = function(prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
}
return this;
}
}
Example:
window.onload = function() {
var compStyle = window.getComputedStyle(document.getElementById('test'), "");
alert(compStyle.getPropertyValue("color"));
alert(compStyle.getPropertyValue("float"));
alert(compStyle.getPropertyValue("background-color"));
}
Here is the solution. It is based on this Trick, but then I've expanded it in case to resolve two problems.
First problem is that borderTopWidth (left,bottom,right) in el.currentStyle returns as adjective - 'thin', 'medium', 'thick' - or 'none'. The Trick will return exception.
And second problem - some values will not be calculated correctly. Such as opacity and some else. You can check it by yourself by applying this Trick-method to all the properties:
var _style = el.currentStyle;
for (var key in _style) {
/// here is the Trick.....
}
At last, here is my solution, based on assumption, that I know all the properties I want to get by this function:
if (!window.getComputedStyle) window.getComputedStyle = function(el){
var __style = el.currentStyle,
_style = {};
for (var i in __style) {
_style[i] = __style[i];
}
// IE8 returns border widths as adjectives
if (style.indexOf("border") === 0)
switch (_style[style]) {
case "thin":
_style[style] = 2;
break;
case "medium":
_style[style] = 4;
break;
case "thick":
_style[style] = 6;
break;
default:
_style[style] = 0;
}
// based on http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
var leftCopy = el.style.left;
var runtimeLeftCopy = el.runtimeStyle.left;
// some properties, that I want to use
_styleParams = {
left : 1,
right : 1,
top : 1,
bottom : 1,
width : 1,
height : 1,
borderLeftWidth : 1,
borderRightWidth : 1,
borderTopWidth : 1,
borderBottomWidth : 1,
paddingLeft : 1,
paddingRight : 1,
paddingTop : 1,
paddingBottom : 1,
marginLeft : 1,
marginRight : 1,
marginTop : 1,
marginBottom : 1
}
for (var key in _styleParams) {
el.runtimeStyle.left = el.currentStyle.left;
el.style.left = _style[key];
_style[key] = el.style.pixelLeft;
el.style.left = leftCopy;
el.runtimeStyle.left = runtimeLeftCopy;
}
// opacity for IE8
if (_style.filter.match('alpha')) {
_style.opacity = _style.filter.substr(14);
_style.opacity = parseInt(_style.opacity.substring(0, _style.opacity.length-1)) / 100;
} else {
_style.opacity = 1;
}}
Here is more complete polyfill for IE8/getComputedStyle which should handle all cases:
https://github.com/jonathantneal/Polyfills-for-IE8/blob/master/getComputedStyle.js
I used a similar method to my original solution with an additional case to handle inline styles, also the way to check if the current document support the getComputedStyle is a bit different it checks in the document.defaultView instead of the window itself, here is the full function
public static native String getStyleProperty(Element el, String prop) /*-{
var computedStyle;
if (document.defaultView && document.defaultView.getComputedStyle) { // standard (includes ie9)
computedStyle = document.defaultView.getComputedStyle(el, null)[prop];
} else if (el.currentStyle) { // IE older
computedStyle = el.currentStyle[prop];
} else { // inline style
computedStyle = el.style[prop];
}
return computedStyle;
}-*/;
source
The best solution I've found so far was from another answer here: https://stackoverflow.com/a/17890142/3672465. It's a standalone version of the jQuery curCSS() code; you may need to adjust it to suit your needs (as Maksim notes in his answer). Here's a compact version of the IE 8 portion, if you just want something to drop in.
if( !window.getComputedStyle && document.documentElement.currentStyle ){
function getStyles( elem ){ return elem.currentStyle; };
function curCSS( elem, name, computed ){
var rnum = /^([+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|))(?!px)[a-z%]+$/i;
var t = 'left', l, rs, rsL, c = computed || getStyles( elem ),
r = c ? c[ name ] : undefined, s = elem.style;
if( r == null && s && s[ name ] ){ r = s[ name ]; }
if( rnum.test( r ) && !/^(top|right|bottom|left)$/.test( name ) ){
l = s[t]; rs = elem.runtimeStyle; rsL = rs && rs[t];
if( rsL ){ rs[t] = elem.currentStyle[t]; }
s[t] = name === 'fontSize' ? '1em' : r; r = s.pixelLeft + 'px';
s[t] = l; if( rsL ){ rs[t] = rsL; }
}
return r === '' ? 'auto' : r;
};
}