Need some JavaScript explanation for this code - javascript

I know that it is bad practice to write code like this:
var createBox = function(width, height, margin){
alert("Margin is set to " + margin);
//margin is undefined in this context or why?
var margin = margin || 2;
alert("Margin is now " + margin);
}
createBox(0,0,0);
But can someone please explain, why margin is always set to 2?
Is it because it is undefined in the direct context of initializing a variable with the same name inside the function?
edit: sorry, I got the problem wrong ...
Please give a small hint :)
Regards, Tom

The || operator in JavaScript returns the value of the first operand if the first operand is truthy. Otherwise it returns the value of the second operand. It doesn't return 1/0, or true/false, as in some other languages.
Therefore when the margin argument holds a falsy value, such as 0 or undefined, it will return 2, since these are both falsy values in JavaScript.
The falsy values in JavaScript are: an empty string "", the null value, a value of 0, the NaN value, the boolean value false, and also undefined.
What you describe is a very common idiom in JavaScript. In fact the || operator is sometimes called the default operator1. You can use it to assign default values to variables when they are undefined. The problem in this case is that since 0 is a valid argument, the default operator is not behaving as required. You may want to do the following instead:
margin = typeof margin === 'undefined' ? 2 : margin;
1 Douglas Crockford: The Elements of JavaScript Style - Part 2 - Idioms.

If you call createBox(0,0,0), then margin is 0 (which has the truth value of false), so the expression margin || 2 becomes 0 || 2 which is equal to 2.

0 evaluates to false List of Truthy Values

// This function creates a new box by receiving three parameters
var createBox = function(width, height, margin){
// Output the margin of the box, zero in current case
alert("Margin is set to " + margin);
// If the margin is zero or undefined, '' set default value to 2
var margin = margin || 2;
// Output the new value of the margin which is 2
alert("Margin is now " + margin);
}
// here will show Margin: 0 after that Margin: 2
createBox(0,0,0);
// here will show Margin: 1 after that Margin: 1
createBox(0,0,1);
// here will show Margin: 3 after that Margin: 3
createBox(1,2,3);

Related

Get elements heights using JS and pass into CSS variable

I want to get the height of an element and pass it into a css variable. The issue im having is when i try to pass that number into the navHeight variable it ends up returning as undefined.
<script>
let navHeightTest = document.getElementById("navbar").offsetHeight;
console.log(navHeightTest); // Returns the nav height as a number
let navHeight = Document.documentElement.style.setProperty('--nav-height', navHeightTest);
console.log(`Nav Height=${navHeight}`); // Returns undefined
</script>
CSS
:root {
--nav-height: 101px; /* Default Fallback Value */
}
.dynamic-margin {
margin-top: calc(var(--nav-height) + 2.5rem) !important;
}
Good question - this can get confusing because setProperty returns undefined, not the value you just set.
Check out the Return value entry in the docs: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty#return_value
If you observe that the --nav-height variable isn't being updated either it might be because you're missing the units when you call setProperty. Since your default in the CSS is in pixels, and offsetHeight reports the number in pixels, I'm guessing you want pixels. Note the 'px' at the very end of the line.
let navHeight = Document.documentElement.style.setProperty('--nav-height', navHeightTest + 'px');

How to determine if a binary tree is balanced or not using recursion?

I am trying to return true if the tree is balanced and false if not, I came up with this
recursive solution below but I am not correct the correct boolean. I feel that it makes
sense to compare the highest height of the tree vs the lower height? Not sure where I am going wrong
function tree (rootNode) {
// Your code here
if (!rootNode) return 0;
if (!rootNode.left && !rootNode.right) return 0;
let minHeigth = 1 + Math.min(tree(rootNode.left), tree(rootNode.right))
let maxHeigth = 1 + Math.max(tree(rootNode.left), tree(rootNode.right))
if(maxHeigth - minHeigth <= 1){
return true
}else{
return false
}
}
My recursive algorithm will be like this.
function isBalanced(rootNode){
if(rootNode == null){
return true;
}
if(checkHeight(rootNode) === -1){
return false;
} else{
return isBalanced(root.left) && isBalanced(root.right);
}
}
and the helper method checkHeight which will check height will be
function checkHeight(rootNode){
if(root ==null){
return 0;
}
let leftHeight=checkHeight(root.left);
let rightHeight=checkHeight(root.right);
if(Math.abs(leftHeight - rightHeight) > 1){
return -1;
}
else{
return Math.max(leftHeight,rightHeight) +1;
}
}
NOTE: This Algo will have the time complexity of O(N)
The main issue is that your function is mixing two things:
Returning the height (number)
Returning whether it is balanced (boolean)
If the ultimate purpose is to return a boolean, then you need a different function for getting the height (a number).
Secondly, in determining the height the first two lines of your code show an inconsistency:
for a null (empty) tree it is determined that its height is 0
for a node without children, it is also determined that its height is 0
Yet these two trees have a different height. If a single node (root) is considered to have height 0, then an empty tree has height -1. This is in line with Wikipedia:
The height of a node is the length of the longest downward path to a leaf from that node. The height of the root is the height of the tree. The depth of a node is the length of the path to its root (i.e., its root path). This is commonly needed in the manipulation of the various self-balancing trees, AVL Trees in particular. The root node has depth zero, leaf nodes have height zero, and a tree with only a single node (hence both a root and leaf) has depth and height zero. Conventionally, an empty tree (tree with no nodes, if such are allowed) has height −1.

Why does this expression equal NaN, but equal a valid answer when defined somewhere else?

So I'm writing a game on JS Canvas and I'm making my own GUI from scratch. To do so, I made a button object with fields x, y, width, height and intersects(click_event). For some reason, when I directly put this expression for x, it returns NaN even though the expression works everywhere else.
It's just a simple game on Canvas. I know I could probably use some dirty trick to work around it, but I want to keep my code clean. I just don't understand why this wouldn't work.
var button = {
height:80,
width:200,
x:canvas.width/2 - this.width/2, //this is the problem
y:200,
//other stuff
};
console.log(button.x); //this prints "NaN"
console.log(canvas.width/2 - button.width/2); //prints correct num
The canvas width is 1000, so 1000 / 2 - 200 / 2 should equal 400, which it does when called inside console.log.
But when I put it inside button.x it evaluates to NaN.
You can't access/reference a property within a object during initialization.
So this will never work:
var myObject = {
height: 2
doubleHeight: 2 * this.height
}
One solution would be to add the poperty after you have initialized the object. Your code would like this:
var button = {
height:80,
width:200,
y:200,
//other stuff
};
button.x = canvas.width/2 - button.width/2
Another solution would be to wrap inside function
function createButton(height, width, canvasWidth) {
return {
height: height,
width: width,
y: width,
x: canvasWidth/2 - width/2
}
}
It can be achieved by using constructor function
var button = new function() {
this.height=80;
this.width=200;
this.x = canvas.width/2 - this.width/2;
this.y=200;
}

Coffeescript: Conditional variable in for loop not declared correctly

I am using coffeescript to render a plot graph. I had previously posted a question about setting up the conditionals; which I believe is now solved. However, the variable curr_visibility, used for one of the conditionals, is causing an issue I think, because it is not defined correctly. The graph plot essentially works like this; a 0 (not visible) or 1 (visible) is assigned to each point on the graph (the points are used to draw a line that is essentially a terrain profile coming from a map using a DEM image). I am attaching a screenshot which illustrates my bug (LV = lastVisibilty and CV = curr_visibility). The variable curr_visibility is inside a for loop. I need to make sure that it is updated after each iteration, but I am just not sure it is set up properly to work inside my fillColor: if conditional statement. the code starts with two empty sets- line = [] and datasets = [] Plot graph showing the bug. The area between LV and CV should be red for No visibility
prev_visibility = data[0].visibility
for elem, index in data
curr_visibility = elem.visibility
point = [
index
elem.geometry[2]
]
line.push point
unless prev_visibility is curr_visibility
datasets.push line
line = [point]
prev_visibility = curr_visibility
datasets.push line
line = []
lastVisibility = data[0].visibility
newfillColor = if lastVisibilty == 0 && curr_visibility == 0
"#C90E30"
else if lastVisibilty == 0 && curr_visibility == 1
"#439C32"
else if lastVisibilty == 1 && curr_visibility == 0
"#C90E30"
else
"#439C32"
for set in datasets
line.push
data: set,
lines:
show: true
fill: true
opacity: 0.7
fillColor: newfillColor
lastVisibility = 1 - lastVisibility
OK, with the help of a coworker, I was able to resolve this issue. First, in the code above, every instance of the variable prev_visibility was removed. It was determined not to be necessary. Second, we determined that index method needed to be utilized to relate to a new variable, next_visibility, that would compare the current visibility value of a point to that of the next for every iteration (I hope I'm explaining this correctly). To do this, we added the following:
line.push point
if (index + 1) < data.length
next_visibility = data[index + 1].visibility
else
next_visibility = curr_visibility
unless next_visibility is curr_visibility
datasets.push line
line = [point]
Finally, all of the newFillColor stuff was removed and I reverted back to fillColor: if lastVisibility is 0 then "#C90E30" else "439C32"

convert css units

i'm trying to get back a style property in all valid 'length' and 'percent' units, converted from the original value set for that property.
e.g., if i have a div with style.width set to 20%, i'd want back an object with that value in percent (of course, 20%), pixels (whatever the actual pixel width is), em, pt, ex, etc.
i realize that 'percentage' is not a 'length' value, and that not all properties that accept length values accept percentage, but want to include that as well.
of course, some values will be dependent on the element specifically, and possibly it's position in the DOM (e.g., getting the em value will require that element's parent computed font size as well).
i can assume that the style is set explicitly for the element - i'm aware of how to retrieve the current computed style of an element - i'm just hoping to not repeat work someone else has probably already done. i'm also aware of http://www.galasoft.ch/myjavascript/WebControls/css-length.html, but it relies on style.pixelWidth or node.clientWidth, and fails in Chrome (I'd assume it fails in Safari as well... and probably others).
i've already got color values worked out (rgb, rgba, hex, name) - this is of course a lot more straightforward. i'm working with values that are mathematically mutable, so really only need 'length' and 'percent' values (if called on a property set with a non-length, non-percent value - like 'font-size: larger' - the function could fail, or throw an error).
if written procedurally, something like this would be ideal:
function getUnits(target, prop){
var value = // get target's computed style property value
// figure out what unit is being used natively, and it's values - for this e.g., 100px
var units = {};
units.pixel = 100;
units.percent = 50; // e.g., if the prop was height and the parent was 200px tall
units.inch = 1.39; // presumably units.pixel / 72 would work, but i'm not positive
units.point = units.inch / 72;
units.pica = units.point * 12;
// etc...
return units;
}
I'm not asking for someone to write code for me, but my hope is that someone has already done this before and it's available in some open-source library, framework, blog post, tut, whatever. failing that, if someone has a clever idea how to streamline the process, that'd be great as well (the author of the link above created a temporary div and computed a single value to determine the ratios for other units - a handy idea but not one i'm entirely sold on, and definitely one that'd need supplemental logic to handle everything i'm hoping accept).
thanks in advance for any insight or suggestions.
EDIT: updated to allow user to pick a single unit to be returned (e.g., exists as %, get back in px) - big improvement in performance for when that's enough - might end up changing it to just accept a single unit to convert, and get rid the loops. Thanks to eyelidlessness for his help. /EDIT
this is what i've come up with - after preliminary testing it appears to work. i borrowed the temporary div idea from the link mentioned in the original question, but that's about all that was taken from that other class.
if anyone has any input or improvements, i'd be happy to hear it.
(function(){
// pass to string.replace for camel to hyphen
var hyphenate = function(a, b, c){
return b + "-" + c.toLowerCase();
}
// get computed style property
var getStyle = function(target, prop){
if(prop in target.style){ // if it's explicitly assigned, just grab that
if(!!(target.style[prop]) || target.style[prop] === 0){
return target.style[prop];
}
}
if(window.getComputedStyle){ // gecko and webkit
prop = prop.replace(/([a-z])([A-Z])/, hyphenate); // requires hyphenated, not camel
return window.getComputedStyle(target, null).getPropertyValue(prop);
}
if(target.currentStyle){ // ie
return target.currentStyle[prop];
}
return null;
}
// get object with units
var getUnits = function(target, prop, returnUnit){
var baseline = 100; // any number serves
var item; // generic iterator
var map = { // list of all units and their identifying string
pixel : "px",
percent : "%",
inch : "in",
cm : "cm",
mm : "mm",
point : "pt",
pica : "pc",
em : "em",
ex : "ex"
};
var factors = {}; // holds ratios
var units = {}; // holds calculated values
var value = getStyle(target, prop); // get the computed style value
var numeric = value.match(/\d+/); // get the numeric component
if(numeric === null) { // if match returns null, throw error... use === so 0 values are accepted
throw "Invalid property value returned";
}
numeric = numeric[0]; // get the string
var unit = value.match(/\D+$/); // get the existing unit
unit = (unit == null) ? "px" : unit[0]; // if its not set, assume px - otherwise grab string
var activeMap; // a reference to the map key for the existing unit
for(item in map){
if(map[item] == unit){
activeMap = item;
break;
}
}
if(!activeMap) { // if existing unit isn't in the map, throw an error
throw "Unit not found in map";
}
var singleUnit = false; // return object (all units) or string (one unit)?
if(returnUnit && (typeof returnUnit == "string")) { // if user wants only one unit returned, delete other maps
for(item in map){
if(map[item] == returnUnit){
singleUnit = item;
continue;
}
delete map[item];
}
}
var temp = document.createElement("div"); // create temporary element
temp.style.overflow = "hidden"; // in case baseline is set too low
temp.style.visibility = "hidden"; // no need to show it
target.parentNode.appendChild(temp); // insert it into the parent for em and ex
for(item in map){ // set the style for each unit, then calculate it's relative value against the baseline
temp.style.width = baseline + map[item];
factors[item] = baseline / temp.offsetWidth;
}
for(item in map){ // use the ratios figured in the above loop to determine converted values
units[item] = (numeric * (factors[item] * factors[activeMap])) + map[item];
}
target.parentNode.removeChild(temp); // clean up
if(singleUnit !== false){ // if they just want one unit back
return units[singleUnit];
}
return units; // returns the object with converted unit values...
}
// expose
window.getUnits = this.getUnits = getUnits;
})();
tyia
Check out Units, a JavaScript library that does these conversions.
Here's a blog post by the author describing the code.
Late to the party and I don't think this necessarily answers the question fully because I haven't included conversion of percentages. However, I do think it's a good start that can be easily modified for your specific usage.
Javascript function
/**
* Convert absolute CSS numerical values to pixels.
*
* #link https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#numbers_lengths_and_percentages
*
* #param {string} cssValue
* #param {null|HTMLElement} target Used for relative units.
* #return {*}
*/
window.convertCssUnit = function( cssValue, target ) {
target = target || document.body;
const supportedUnits = {
// Absolute sizes
'px': value => value,
'cm': value => value * 38,
'mm': value => value * 3.8,
'q': value => value * 0.95,
'in': value => value * 96,
'pc': value => value * 16,
'pt': value => value * 1.333333,
// Relative sizes
'rem': value => value * parseFloat( getComputedStyle( document.documentElement ).fontSize ),
'em': value => value * parseFloat( getComputedStyle( target ).fontSize ),
'vw': value => value / 100 * window.innerWidth,
'vh': value => value / 100 * window.innerHeight,
// Times
'ms': value => value,
's': value => value * 1000,
// Angles
'deg': value => value,
'rad': value => value * ( 180 / Math.PI ),
'grad': value => value * ( 180 / 200 ),
'turn': value => value * 360
};
// Match positive and negative numbers including decimals with following unit
const pattern = new RegExp( `^([\-\+]?(?:\\d+(?:\\.\\d+)?))(${ Object.keys( supportedUnits ).join( '|' ) })$`, 'i' );
// If is a match, return example: [ "-2.75rem", "-2.75", "rem" ]
const matches = String.prototype.toString.apply( cssValue ).trim().match( pattern );
if ( matches ) {
const value = Number( matches[ 1 ] );
const unit = matches[ 2 ].toLocaleLowerCase();
// Sanity check, make sure unit conversion function exists
if ( unit in supportedUnits ) {
return supportedUnits[ unit ]( value );
}
}
return cssValue;
};
Example usage
// Convert rem value to pixels
const remExample = convertCssUnit( '2.5rem' );
// Convert time unit (seconds) to milliseconds
const speedExample = convertCssUnit( '2s' );
// Convert angle unit (grad) to degrees
const emExample = convertCssUnit( '200grad' );
// Convert vw value to pixels
const vwExample = convertCssUnit( '80vw' );
// Convert the css variable to pixels
const varExample = convertCssUnit( getComputedStyle( document.body ).getPropertyValue( '--container-width' ) );
// Convert `em` value relative to page element
const emExample = convertCssUnit( '2em', document.getElementById( '#my-element' ) );
Current supported formats
Any format with preceding plus (+) or minus (-) symbol is valid, along with any of the following units: px, cm, mm, q, in, pc, pt, rem, em, vw, vh, s, ms, deg, rad, grad, turn
For example:
10rem
10.2em
-0.34cm
+10.567s
You can see a full combination of formats here: https://jsfiddle.net/thelevicole/k7yt4naw/1/
Émile kind of does this, specifically in its parse function:
function parse(prop){
var p = parseFloat(prop), q = prop.replace(/^[\-\d\.]+/,'');
return isNaN(p) ? { v: q, f: color, u: ''} : { v: p, f: interpolate, u: q };
}
The prop argument is the computedStyle for some element. The object that's returned has a v property (the value), an f method that is only used later on for animation, and a u property (the unit of the value, if necessary).
This doesn't entirely answer the question, but it could be a start.
While digging through the SVG spec, I found that SVGLength provides an interesting DOM API for builtin unit conversion. Here's a function making use of it:
/** Convert a value to a different unit
* #param {number} val - value to convert
* #param {string} from - unit `val`; can be one of: %, em, ex, px, cm, mm, in, pt, pc
* #param {string} to - unit to convert to, same as `from`
* #returns {object} - {number, string} with the number/string forms for the converted value
*/
const convert_units = (() => {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
const len = rect.width.baseVal;
const modes = {
"%": len.SVG_LENGTHTYPE_PERCENTAGE,
"em": len.SVG_LENGTHTYPE_EMS,
"ex": len.SVG_LENGTHTYPE_EXS,
"px": len.SVG_LENGTHTYPE_PX,
"cm": len.SVG_LENGTHTYPE_CM,
"mm": len.SVG_LENGTHTYPE_MM,
"in": len.SVG_LENGTHTYPE_IN,
"pt": len.SVG_LENGTHTYPE_PT,
"pc": len.SVG_LENGTHTYPE_PC,
};
return (val, from, to, context) => {
if (context)
context.appendChild(rect);
len.newValueSpecifiedUnits(modes[from], val);
len.convertToSpecifiedUnits(modes[to]);
const out = {
number: len.valueInSpecifiedUnits,
string: len.valueAsString
};
if (context)
context.removeChild(rect);
return out;
};
})();
Usage example:
convert_units(1, "in", "mm");
// output: {"number": 25.399999618530273, "string": "25.4mm"}
Some units are relative, so need to be placed in a parent DOM element temporarily to be able to resolve the unit's absolute value. In those cases provide a fourth argument with the parent element:
convert_units(1, "em", "px", document.body);
// output: {"number": 16, "string": "16px"}

Categories

Resources