Academic vs Intuitive understanding (aka practice makes competent)
Tags: javascript, beginners
Published: 2022-05-08 16:37:46


Welcome to my first blog post!

I am just starting out on my journey as a student at Flatiron School, and I'd like to share a few insights that I've picked up so far along the way. Specifically, I'd like to discuss how much there is to gain from practicing your code and seeing how it works over just learning the rules, syntax, etc.

One thing that jumps out at me, again and again, is the vast gap between having _knowledge_ (i.e. academic understanding) of something and actually _getting_ (intuitive understanding) something in a way that the knowledge can be applied properly and practically. This in fact appears to be a major difference between the way Flatiron School teaches and the way many college courses are taught. Your average college course may teach you all there is to know about programming; Flatiron School _makes you into a programmer_. The difference is subtle but profound.

I'll give a quick example of this to demonstrate what I mean. It's hard for me to imagine now, but when I completed my prework for Flatiron I had no idea how iteration/looping could be useful. That's not to say that I didn't understand the basics of how it worked, but I hadn't intuitively grasped how it could actually be applied in a meaningful fashion. Because of this, when I was creating a website for my prework project, it never occurred to me that this:

```
flipCards(lineOne, 0, 0);
flipCards(lineOne, 1, 1);
flipCards(lineOne, 2, 2);
flipCards(lineOne, 3, 3);
flipCards(lineOne, 4, 4);
flipCards(lineOne, 5, 5);
flipCards(lineTwo, 0, 6);
flipCards(lineTwo, 1, 7);
flipCards(lineTwo, 2, 8);
flipCards(lineTwo, 3, 9);
flipCards(lineTwo, 4, 10);
flipCards(lineTwo, 5, 11);
flipCards(lineThree, 0, 12);
flipCards(lineThree, 1, 13);
flipCards(lineThree, 2, 14);
flipCards(lineThree, 3, 15);
flipCards(lineThree, 4, 16);
flipCards(lineThree, 5, 17);
flipCards(lineFour, 0, 18);
flipCards(lineFour, 1, 19);
flipCards(lineFour, 2, 20);
flipCards(lineFour, 3, 21);
flipCards(lineFour, 4, 22);
flipCards(lineFour, 5, 23);
```
with only a few minor modifications to my code could have looked like this:

```
for (let i = 0; i < allCards.length; i++) {
    flipCards(i)
}
```
Now I rarely write code without using some form of iteration or looping but at the time it wasn't so simple.

So how do you make the jump? How do you go from academic knowledge to intuitive understanding? I've found the answer to be practice, or more specifically _making mistakes_. "Learning from your mistakes" is a cliché that may be older than computer programming itself, but rarely can it be applied quite as literally as it can when it comes to coding. By working through your code and figuring out what it's actually doing vs what you think it should be doing you can gain more than you ever would by "book" learning alone. Allow me to take you through some code that's based on code that I wrote, and demonstrate how at every step every mistake I made led me to a deeper and more complete understanding of the code I was working with.

Say we have an `object` with car manufacturers as its keys, some with empty `strings` as values, some with an `array` containing some of the models of car that are produced by that company:

```
const cars = {
    honda: '',
    toyota: ['corolla', 'camry', 'avalon', 'sienna'],
    gmc: '',
    chrysler: '',
    ford: ['explorer', 'fusion'],
    hyundai: ['elantra', 'sonata', 'genesis', 'santa fe'],
    infiniti: ''
};
```
Now let's say I'd like to render these cars as a `<ul>` on my webpage, with a nested `<ul>` to list the models of car that are included for those manufactures that have them. If we were just making a list of manufacturers (the keys) that would be fairly simple. We have a `<ul>` in our `html` file with an `id` of `#main-list`. Let's build a function that would create a `<li>` tag and add it to this `<ul>`:

```
function makeLi(car) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById('main-list').appendChild(li)
}
```
Now we can use a simple `for...in` to create the list:

```
for (const car in cars) {
    makeLi(car)
}
```
Now let's see what we get:



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqgtxn7yc5umvb9wyl2b.png)
That was easy!

However, we'd like to add the nested `<ul>` so let's see how we can do that.

First let's add another parameter to `makeLi`:

```
function makeLi(car, id) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById(id).appendChild(li)
}
```
Everything is the same, except that the `id` of the `<ul>` is passed as an argument instead of being spelled out in the function.

Now we'll start building another function. This one will start off the same as `makeLi` but will have a few extra steps:

```
function makeLiWithNestedUl(car) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById('main-list').appendChild(li)
    const ul = document.createElement('ul')
    ul.id = 'child-ul'
    li.appendChild(ul)
    cars.car.map(model => makeLi(model, 'child-ul'))
}
```
Now let's update our `for..in` with a simple ternary statement:

```
for (const car in cars) {
    cars.car === '' ? makeLi(car, 'main-list') : makeLiWithNestedUl(car)
}
```
And let's see what we get:



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bf2yn0lwxk4myysz6t9.png)
Well... Seems like we just ran into our first learning opportunity! The error message is telling us that JavaScript apparently doesn't recognize our `.map()` method for what it is, rather it seems to be trying to give the word "map" some sort of value (which doesn't exist). It must be that `cars.car`, which should return the value of whichever key our `for..in` loop is passing as an argument to the function, is not being recognized as an `array` for some reason. Let's use `console.log()` to do some troubleshooting.

```
for (const car in cars) {
    console.log(cars.car)
    //cars.car === '' ? makeLi(car, 'main-list') : makeLiWithNestedUl(car)
}
```
Let's take a look at our console to see what we've gotten:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prrskofrjuc8t3bkpry5.png)
That's weird... It seems that `cars.car` is returning `undefined`. Have we misunderstood something about our `for..in` loop? We'll quickly make sure we haven't missed something here:

```
for (const car in cars) {
    console.log(car)
    //cars.car === '' ? makeLi(car, 'main-list') : makeLiWithNestedUl(car)
}
```
Let's check the console:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/grrbb25g8hse5tti6ps6.png)
It seems that `car` does in fact return the keys to our `cars` object, so why is `cars.car` undefined? We've just learned our first lesson! Now I've realized the implications of something I already knew but perhaps never internalized the way I needed to. All keys are really `strings`. Our `for..in` is saving the keys of `cars` to the `car` variable _as strings_, and therefore our dot notation isn't working! In fact, if we look at our DOM, it seems the ternary expression didn't work properly either:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7219erhua3ysld99ch8.png)
It seems that our `<li>` for honda has a nested `<ul>` despite its value in our `cars` object being an empty `string`. Our ternary statement was checking if `cars.car === ''` and _was returning false_, because `cars.car` was in fact `undefined`! Our code therefore called the `makeLiWithNestedUl` function on the first key of our `cars` object, honda, and then ran into an error when we tried using the `.map()` method on `cars.car`, which was `undefined`. Let's do a quick `console.log()`, this time with bracket notation, to confirm our suspicions:

```
for (const car in cars) {
    console.log(cars[car])
    //cars.car === '' ? makeLi(car, 'main-list') : makeLiWithNestedUl(car)
}
```
And here's the result:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0wjst6es84qvqa7eix2.png)
Perfect! It's logging the correct values for all of our keys! (In some cases that's nothing, i.e. an empty `string`.)

Let's get rid of the `console.log()`, uncomment our ternary statement, change our dot notation to bracket notation (both in the ternary and in the `makeLiWithNestedUl` function), and see what happens!


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ngeq032m5xb6y243niln.png)
Oy vey. Our bracket notation seems to have fixed our ternary statement and gotten our `.map()` method to work. However, for some reason, all of the nested `<li>` elements are being added to the first nested `<ul>`, under toyota. Let's take a look at our DOM to see if ford and hyundai aren't getting their `<ul>`s for some reason.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahmqu4xv4tb9y3c6ok6a.png)
Hmmm. They definitely are getting nested `<ul>`s added, but I think our mistake is fairly obvious now. We've accidentally assigned the same `id` to all of our nested `ul`s. There are two lessons to be learned here. First of all, although an `html` document isn't supposed to have more than one element with the same id, JavaScript apparently has no problem adding the same `id` to more than one DOM node if we tell it to. Secondly, if more than one DOM node or element has the same `id`, `document.getElementByID()` will return the first element with that `id`. Let's look at our functions again:

```
function makeLi(car, id) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById(id).appendChild(li)
}
function makeLiWithNestedUl(car) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById('main-list').appendChild(li)
    const ul = document.createElement('ul')
    ul.id = 'child-ul'
    li.appendChild(ul)
    cars[car].map(model => makeLi(model, 'child-ul'))
}
```
Our `makeLiWithNestedUl` is calling `makeLi` and passing `'child-ul'` as the argument for the `id` parameter. Because of this, all of our nested `<li>`s are being added to the first `<ul>` with an `id` of `'child-ul'`, which is the one for toyota.

How can we assign different `id`s to our nested `<ul>`s while still effectively using iteration? Here's where we can use a simple tool that's way more powerful than I realized when I first learned about it. Some simple string interpolation should allow us to assign unique `id`s to each nested `<ul>`:

```
ul.id = `${car}-list`
```
```
cars[car].map(model => makeLi(model, `${car}-list`))
```
And here's our updated function:
```
function makeLiWithNestedUl(car) {
    const li = document.createElement('li')
    li.textContent = car
    document.getElementById('main-list').appendChild(li)
    const ul = document.createElement('ul')
    ul.id = `${car}-list`
    li.appendChild(ul)
    cars[car].map(model => makeLi(model, `${car}-list`))
}
```
Let's refresh our browser and see the result:



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an2seyhot12xld6mdr01.png)
We did it!

Let's go a bit further. There's more code to write, more mistakes to be made, and more lessons to learn!

I'd like to be able to change the color of each car manufacturer name to blue when it's clicked on, and each car model name to red when its clicked on. I'd also like for both to be changed back to black when clicked again. Should be simple, right? We can create a relatively simple event listener. First let's modify our original functions a bit:

```
function makeLi(car, id, className) {
    const li = document.createElement('li')
    li.textContent = car
    li.className = className
    document.getElementById(id).appendChild(li)
}
function makeLiWithNestedUl(car) {
    const li = document.createElement('li')
    li.textContent = car
    li.className = 'parent-li'
    document.getElementById('main-list').appendChild(li)
    const ul = document.createElement('ul')
    ul.id = `${car}-list`
    li.appendChild(ul)
    cars[car].map(model => makeLi(model, `${car}-list`, 'child-li'))
}
for (const car in cars) {
    cars[car] === '' ? makeLi(car, 'main-list', 'parent-li') : makeLiWithNestedUl(car)
}
```
What I did here was add a parameter to my `makeLi` function allowing me to assign a `className` to the `<li>` element being created. This allows me to to assign separate `classNames` for the parent and child `<li>` elements. Lets make some `arrays` now:

```
const parentLiArray = Array.from(document.getElementsByClassName('parent-li'))
const childArray = Array.from(document.getElementsByClassName('child-li'))
```
Now let's add some event listeners:

```
parentLiArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'blue' ? li.style.color = 'black' : li.style.color = 'blue'
    })
})

childArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'red' ? li.style.color = 'black' : li.style.color = 'red'
    })
})
```
Great! now let's check the result:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6keqkdgva31aaumxny6.png)
Oops. We forgot that styles applied to a parent will also apply to the child unless otherwise specified. When we click on hyundai it's also turning its child elements blue. Let's give a default style to all of our `<ul>` elements to prevent this:

```
Array.from(document.getElementsByTagName('ul')).map(ul => ul.style.color = 'black')
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxuanqfe6q1t3ho5pvrw.png)
There we go! Uh-oh. We seem to be running into other issues though.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inch3lpoxfnhmvu7olaf.png)
Why are our nested `<li>`s also turning blue? To make matters worse, they're not changing back to black when they are clicked again. Let's get some `console.log()`s going again.

```
parentLiArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'blue' ? li.style.color = 'black' : li.style.color = 'blue'
        console.log(e.target.style.color)
    })
})

childArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'red' ? li.style.color = 'black' : li.style.color = 'red'
        console.log(e.target.style.color)
    })
})
```
Now we can check the console for some clues as to what's going on:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/etfk329s0mej3f8hzzab.png)
Now that's interesting. There are _two_ values being logged when the nested `<li>` is being clicked. The first is red, as it should be. The second is blue. Apparently our code is changing the color to red as it should, but then immediately changing it to blue. Clicking it again logs the same result. (You can take my word for it). Let's try a different `console.log()` to see if our event listeners are behaving the way they should:

```
parentLiArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'blue' ? li.style.color = 'black' : li.style.color = 'blue'
        console.log(e.target)
    })
})

childArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        li.style.color === 'red' ? li.style.color = 'black' : li.style.color = 'red'
        console.log(e.target)
    })
})
```
Let's check out the console again:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvxu9y6hp9mvzcryfmm6.png)
Fascinating. The clicked element is being logged twice! Making this mistake originally taught me something I actually hadn't known. Adding an event listener to a parent element will automatically add the same event listener to its children. When I clicked the child `<li>` element, it first fired its own event listener, which checked if its style was set to red (it wasn't) and then changed it to red. That part worked perfectly. The problem was that after that the event listener for the parent fired, checked if the style was blue (it wasn't), and changed it to blue. The reason it didn't change back when clicked again was that it kept repeating the same process, checking if it was red, changing it to red, then checking if it was blue and changing it to blue. Now that we realize this maybe we only need one event listener for both! How can we do that? Let's see... We need some way for JavaScript to differentiate between the parent and child elements and act accordingly. I'm going to get rid of the event listener for the child `<li>` elements because we don't need it anymore. Now let's change up our other event listener a bit:

```
parentLiArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        if (li.className === 'parent-li') {
            li.style.color === 'blue' ? li.style.color = 'black' : li.style.color ='blue'
        } else {
            li.style.color === 'red' ? li.style.color = 'black' : li.style.color = 'red'
        }
    })
})
```
Now our event listener is first checking the `className` of its target and acting accordingly, changing between blue and black if its `className` is `'parent-li'`, and changing between red and black if not. Let's try it in our browser again:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxu4j9dk63el69jurbeg.png)
Great! Now let's click a few of them again to make sure they change back to black:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bo6mw0i974r5zmaneegx.png)
Perfect! We did it! Here's the completed, functioning code:

```
const cars = {
    honda: '',
    toyota: ['corolla', 'camry', 'avalon', 'sienna'],
    gmc: '',
    chrysler: '',
    ford: ['explorer', 'fusion'],
    hyundai: ['elantra', 'sonata', 'genesis', 'santa fe'],
    infiniti: ''
};

function makeLi(car, id, className) {
    const li = document.createElement('li')
    li.textContent = car
    li.className = className
    document.getElementById(id).appendChild(li)
}
function makeLiWithNestedUl(car) {
    const li = document.createElement('li')
    li.textContent = car
    li.className = 'parent-li'
    document.getElementById('main-list').appendChild(li)
    const ul = document.createElement('ul')
    ul.id = `${car}-list`
    li.appendChild(ul)
    cars[car].map(model => makeLi(model, `${car}-list`, 'child-li'))
}
for (const car in cars) {
    cars[car] === '' ? makeLi(car, 'main-list', 'parent-li') : makeLiWithNestedUl(car)
}

Array.from(document.getElementsByTagName('ul')).map(ul => ul.style.color = 'black')

const parentLiArray = Array.from(document.getElementsByClassName('parent-li'))

parentLiArray.map(li => {
    li.addEventListener('click', e => {
        const li = e.target
        if (li.className === 'parent-li') {
            li.style.color === 'blue' ? li.style.color = 'black' : li.style.color ='blue'
        } else {
            li.style.color === 'red' ? li.style.color = 'black' : li.style.color = 'red'
        }
    })
})

```

I've now demonstrated just one small example of how practicing code, messing up, and figuring out what went wrong is the best and perhaps only way to truly understand and internalize how your code is _really_ working. The mistakes I made in this example are based on real mistakes I made and the steps I took to fix them. (It was quite frustrating at the time, let me tell you!) I am just starting out on my journey as a programmer and I am looking forward to many future mistakes! Thanks for hanging in there with me until the end of my first ever blog. Any and all feedback is welcome!