Operator precedence with Javascript Ternary operator

I cant seem to wrap my head around the first part of this code ( += ) in combination with the ternary operator.

h.className += h.className ? ' error' : 'error'

The way i think this code works is as following:

h.className = h.className + h.className ? ' error' : 'error'

But that isn't correct because that gives a error in my console.

So my question is how should i interpet this code correctly?

Answers:

Answer
h.className = h.className + (h.className ? ' error' : 'error')

You want the operator to work for h.className, better be specific about it.
Of course, no harm should come from h.className += ' error', but that's another matter.

Also, note that + has precedence over the ternary operator: JavaScript Operator Precedence

Answer

Think of it this way:

<variable> = <expression> ? <true clause> : <false clause>

The way the statement gets executed is basically as follows:

  1. Does <expression> evaluate to true, or does it evaluate to false?
  2. If <expression> evaluates to true, then the value of <true clause> is assigned to <variable>, <false clause> is ignored, and the next statement is executed.
  3. If <expression> evaluates to false, then <true clause> is ignored and the value of <false clause> is assigned to <variable>.

The important thing to realise with the ternary operator in this and other languages is that whatever code is in <expression> should produce a boolean result when evaluated: either true or false.

In the case of your example replace "assigned to" in my explanation with "added to", or similar for whichever shorthand arithmetic you are using, if any.

Answer

The += does what you want, but in the ternary statement at the right hand of it, it checks if h.className is falsey, which it would be if it was undefined. If it's truthy (i.e. if a class name is already specified), then error is added with a space (i.e. adding a new class), otherwise it's added without the space.

The code could be rewritten as you suggest, but you need to specify that h.className is to be used for truthiness-comparison, rather than for using its actual value, in the ternary operator, so make sure you don't bother with the concatenation of values at the same time as doing your ternary operation:

h.className = h.className + (h.className ? ' error' : 'error');
Answer

The right hand side of the = operator is evaluated left to right. So,

g.className = h.className + h.className ? ' error' : 'error';`

is equivalent to

h.className = (h.className + h.className) ? ' error' : 'error';

To be equivalent to

h.className += h.className ? ' error' : 'error';

you have to separate the ternary statement in parenthesis

h.className = h.className + (h.className ? ' error' : 'error');
Answer
if (h.className) {
    h.className = h.className + ' error';
} else {
    h.className = h.className + 'error';
}

should be equivalent of:

h.className += h.className ? ' error' : 'error';
Answer

I would like to pick explanation of wayne :

<variable> = <expression> ? <true clause> : <false clause>

Lets consider both the cases:

case 1:
h.className += h.className ? 'true' : 'false'     
  • assignment operator works fine and value gets appended
  • when runs for the first time, o/p: false
  • 2nd time. o/p: falsetrue -- values keeps appending

case2: h.className = h.className + h.className ? 'true' : 'false'

  • the result is not same as case 1
  • when runs for the first time, o/p: false
  • 2nd time. o/p: false -- values doesn't keep appending

explanation

In the above code, case 1 works fine

whereas case2:

h.className = h.className + h.className ? 'true' : 'false'
is executed as 
 h.className = (h.className + h.className) ? 'true' : 'false'

h.className + h.className => considered as expression for ternary operator as ternary operator is given higher precedence. so, always the result of the ternary expression is just assigned

You need to define the precedence by using brackets

You need to define the order of evaluation to be considered with the help of brackets for case 2 to work as case 1

h.className = h.className + (h.className ? ' error' : 'error') 
Answer

I know this is a very old question, but I am not 100% happy with any of the answers as they all seem incomplete. So here we go again from first principals:

The user's overall aim:

Summarising the code: "I wish to add an error class name to a string, optionally with a leading space if there are already class names in the string."

Simplest solution

As Kobi pointed out, 5 years ago, having a leading space in class names will not cause any problems with any known browsers, so the shortest correct solution would actually be:

h.className += ' error';

That should have been the actual answer to the actual problem.


Be that as it may, the questions asked were...

1) Why did this work?

h.className += h.className ? ' error' : 'error'

The conditional/ternary operator works like an if statement, that assigns the result of its true or false paths to a variable.

So that code worked because it is evaluated simply as:

if (h.className IS NOT null AND IS NOT undefined AND IS NOT '') 
    h.className += ' error'
else
    h.className += 'error'

2) and why did this break?

h.className = h.className + h.className ? ' error' : 'error'

The question states "that gives a[n] error in my console", which may mislead you into thinking the code does not function. In fact the following code does run, without error, but it simply returns ' error' if the string was not empty and 'error' if the string was empty and so did not meet the requirements.

That code always results in a string that contains only ' error' or 'error' because it evaluates to this pseudo code:

if ((h.className + h.className) IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className = ' error'
else
    h.className = 'error'

The reason for this is that the addition operator (+ to the common folk) has higher "precedence" (6) than the conditional/ternary operator (15). I know the numbers appear backwards

Precedence simply means that each type of operator in a language is evaluated in a particular predefined order (and not just left-to-right).

Reference: Javascript Operator Precedence

How to change the order of evaluation:

Now we know why it fails, you need to know how to make it work.

Some other answers talk about changing the precedence, but you can't. Precedence is hard-wired into the language. That is just a fixed set of rules... However, you can change the order of evaluation...

The tool in our toolbox that can change the order of evaluation is the grouping operator (aka brackets). It does this by ensuring the expressions in the brackets are evaluated before operations outside the brackets. That's all they do, but that's enough.

Brackets work simply because they (grouping operators) have higher precedence than all other operators ("there is now a level 0").

By simply adding brackets you change the order of evaluation to ensure the conditional test is performed first, before the simple string concatenation:

h.className = h.className + (h.className ? ' error' : 'error')

I will now leave this answer to rust unseen among the others :)

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.