> For the complete documentation index, see [llms.txt](https://owen31302.gitbook.io/github-education/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://owen31302.gitbook.io/github-education/become-a-front-end-web-developer-or-udacity-3.-javascript-and-the-dom/3.-javascript-and-the-dom/lesson-3-creating-content-with-javascript.md).

# Lesson 3: Creating Content with JavaScript

## Update Existing Page Content <a href="#header-title" id="header-title"></a>

### 1. Update an Element's Inner HTML <a href="#an-element-s-inner-html" id="an-element-s-inner-html"></a>

顯示某個element底下的html

The `.innerHTML` property sets or returns the HTML content *inside* the selected element (i.e. between the tags).

```javascript
document.querySelector('.card').innerHTML
```

There's also the rarely used `.outerHTML` property. `.outerHTML` represents the HTML element itself, *as well as its children*.

```javascript
<h1 id="pick-me">Greetings To <span>All</span>!</h1>

const innerResults = document.querySelector('#pick-me').innerHTML;
console.log(innerResults); // logs the string: "Greetings To <span>All</span>!"

const outerResults = document.querySelector('#pick-me').outerHTML;
console.log(outerResults); // logs the string: "<h1 id="pick-me">Greetings To <span>All</span>!</h1>"
```

更新innerHTML

```javascript
myElement.innerHTML = 'The <strong>Greatest</strong> Ice Cream Flavors';
```

### 2. Update an Element's Text Content <a href="#an-element-s-text-content" id="an-element-s-text-content"></a>

So `.innerHTML` will get/set an element's *HTML* content. If we just want the text content, we can use the fantastically named `.textContent` property!

The `.textContent` property will:

* set the text content of an element and all its descendants
* return the text content of an element and all its descendants

![](/files/-M6YRo57UIlaFZ4_1CJG)

Check out the `.textContent`'s documentation page on MDN: [textContent docs](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)

更新內容：

```javascript
nanodegreeCard.textContent = "I will be the updated text for the nanodegreeCard element!";
```

{% hint style="warning" %}
textContent會把內容當作plain text輸出，而innerHTML則會parse內文的html tag。
{% endhint %}

### 3. An Element's Text Content - Version 2! <a href="#an-element-s-text-content-version-2" id="an-element-s-text-content-version-2"></a>

`.innerText` will get the ***visible*** text of the element. If CSS is used to hide any text inside that element, `.innerText` ***will not*****&#x20;return that text**, while `.textContent` *will* return it.

innerText顯示apply css之後的文字，而textContent則回傳apply css之前的文字。

## Add New Page Content <a href="#header-title" id="header-title"></a>

之前為修改現有element，現在為新增element。

### 1. Adding Content To The Page <a href="#adding-content-to-the-page" id="adding-content-to-the-page"></a>

As you've already discovered, the `.createElement()` method is a method on the `document` object:

<https://developer.mozilla.org/en-US/docs/Web/API/Document>

<https://developer.mozilla.org/en-US/docs/Web/API/Node>

<https://developer.mozilla.org/en-US/docs/Web/API/Element>

```javascript
// creates and returns a <span> element
document.createElement('span');

// creates and returns an <h3> element
document.createElement('h3');
```

createElement只是建立新增的element，針對想要新增的element target呼叫appendChild才會新增element上去：

```javascript
// create a brand new <span> element
const newSpan = document.createElement('span');

// select the first (main) heading of the page
const mainHeading = document.querySelector('h1');

// add the <span> element as the last child element of the main heading
mainHeading.appendChild(newSpan);
```

![](/files/-M6_i9pCjFOwYdmyLKc6)

{% hint style="warning" %}
**.appendChild()** Needs An Element!
{% endhint %}

This is stated above, but I wanted to call this out, specifically. When you're using the `.appendChild()` method, it must be called on an existing element. To be clear, you can't call this on the `document` object, so the following will result in an error:

```javascript
const newSpan = document.createElement('span');

// causes an error
document.appendChild(newSpan);
```

### 2. Creating Text Nodes <a href="#creating-text-nodes" id="creating-text-nodes"></a>

第二種方式則是產生內文後，再添加到element，多一個步驟：

* creates a paragraph element
* creates a text node
* appends the text node to the paragraph
* appends the paragraph to the tag

```javascript
const myPara = document.createElement('p');
const textOfParagraph = document.createTextNode('I am the text for the paragraph!');

myPara.appendChild(textOfParagraph);
document.body.appendChild(myPara);
```

![](/files/-M6bjAonDdVx0q_FJcmu)

用以下的方式產生一樣的結果：

```javascript
const myPara = document.createElement('p');

myPara.textContent = 'I am the text for the paragraph!';
document.body.appendChild(myPara);
```

{% hint style="warning" %}
如果將同一個element多次appendChild給不同element，則只會append到最後一個指定的element。
{% endhint %}

### 3. Inserting HTML In Other Locations <a href="#inserting-html-in-other-locations" id="inserting-html-in-other-locations"></a>

問題：appendChild只能插入parent的child的最後一個位置。

解法：.insertAdjacentHTML()

Enter the `.insertAdjacentHTML()` method! The `.insertAdjacentHTML()` method has to be called with two arguments:

* the location of the HTML
* the HTML text that is going to be inserted

The first argument to this method will let us insert the new HTML in one of four different locations

* `beforebegin` – inserts the HTML text as a previous sibling
* `afterbegin` – inserts the HTML text as the first child
* `beforeend` – inserts the HTML text as the last child
* `afterend` – inserts the HTML text as a following sibling

```markup
<!-- beforebegin -->
<p>
    <!-- afterbegin -->
    Existing text/HTML content
    <!-- beforeend -->
</p>
<!-- afterend -->
```

Here's how we'd call `.insertAdjacentHTML()`:

```javascript
const mainHeading = document.querySelector('#main-heading');
const htmlTextToAdd = '<h2>Skydiving is fun!</h2>';

mainHeading.insertAdjacentHTML('afterend', htmlTextToAdd);
```

Check out the documentation page for more information: [insertAdjacentHTML docs](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)

![](/files/-M6bnylrS3fuxctuqdSL)

## Remove Page Content <a href="#header-title" id="header-title"></a>

### 1. Removing a Child Element <a href="#removing-a-child-element" id="removing-a-child-element"></a>

相對appendChild，也有removeChild

```javascript
<parent-element>.removeChild(<child-to-remove>);
```

Here's the `.removeChild()` documentation page on MDN: [removeChild docs](https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild)

![](/files/-M6cFvjF70zNUYJyX12P)

可以看出來first child其實有奇怪的內容

![](/files/-M6cG-tevFKLW83vCP7r)

到element點選右鍵可以用Edit as HTML去看其底下真正的HTML。

![](/files/-M6cG3RCsu7sRn8fkncM)

### 2. Removing a Child Element (Part 2!) <a href="#removing-a-child-element-part-2" id="removing-a-child-element-part-2"></a>

#### A drawback (and workaround!) with the `.removeChild()` Method <a href="#a-drawback-and-workaround-with-the-removechild-method" id="a-drawback-and-workaround-with-the-removechild-method"></a>

Just like the `.appendChild()` method, there's a (somewhat minor) drawback to the `.removeChild()` method. Both methods:

* require access to parent to function

可以利用parentElement方法來做到這件事：

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.parentElement.removeChild(mainHeading);
```

不需要parent的remove方式：

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.remove();
```

### Remove Page Content Recap <a href="#remove-page-content-recap" id="remove-page-content-recap"></a>

In this short section, we learned two ways to remove an element from the page. You learned about:

* `.removeChild()`
* `.remove()`

The difference is that with `.removeChild()` must be called on the parent of the element being removed and must be passed the child to be removed, while `.remove()` can be called directly on the element to delete.

We also learned about the following helpful properties:

* `.firstChild`
* `.firstElementChild`
* `.parentElement`

The difference between `.firstChild` and `.firstElementChild`, is that `.firstElementChild` will always return the first element, while `.firstChild` *might* return whitespace (if there is any) to preserve the formatting of the underlying HTML source code.

## Style Page Content <a href="#header-title" id="header-title"></a>

### CSS Specificity <a href="#css-specificity" id="css-specificity"></a>

廣度概念：

**Rules in a stylesheet** > **Rules in a \<style tag>** > **Rules in a tag's style attribute**

根據[MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity):

> "specificity" is: the means by which browsers decide which CSS property values are the most relevant to an element and, therefore, will be applied.

> Basically, the closer the style rule is to an element, the more specific it is. For example, a rule in a style attribute on an element will override a style rule for that element in a CSS stylesheet. There is also the specificity of the *type* of selector being used. An \_ID\_ is more specific than a *class*.

這個概念非常重要，有很多地方可以修改style，所以必須知道誰可以override誰。

### 1. Modifying an Element's Style Attribute <a href="#modifying-an-element-s-style-attribute" id="modifying-an-element-s-style-attribute"></a>

修改element其中一個style：

When trying to style an element, the most-specific rules that you can write for an element are written in that element's `style` attribute. Lucky for us, we can access an element's `style` attribute using the `.style` property!

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.style.color = 'red';
```

Check out the documentation page for more information: [style docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)

![](/files/-M6r8xrlj2TRkC4OUXc2)

顯示所有可以指定的field

```javascript
$0.style
```

![](/files/-M6r9_Gfq2jHBM8asUoV)

### 2. Adding Multiple Styles At Once <a href="#adding-multiple-styles-at-once" id="adding-multiple-styles-at-once"></a>

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.style.color = 'blue';
mainHeading.style.backgroundColor = 'orange';
mainHeading.style.fontSize = '3.5em';
```

Fortunately, we can use the `.style.cssText` property to set multiple CSS styles at once!

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.style.cssText = 'color: blue; background-color: orange; font-size: 3.5em';
```

Notice that when using the `.style.cssText` property, you write the CSS styles just as you would in a stylesheet; so you write `font-size` rather than `fontSize`. This is different than using the individual `.style.<property>` way.

### 3. Setting An Element's Attributes <a href="#setting-an-element-s-attributes" id="setting-an-element-s-attributes"></a>

Another way to set styles for an element is to bypass the `.style.<property>` and `.style.cssText` properties altogether and use the `.setAttribute()` method:

```javascript
const mainHeading = document.querySelector('h1');

mainHeading.setAttribute('style', 'color: blue; background-color: orange; font-size: 3.5em;');
```

[style docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)

setAttribute也可以用來賦予class

```javascript
const mainHeading = document.querySelector('h1');

// add an ID to the heading's sibling element
mainHeading.nextElementSibling.setAttribute('id', 'heading-sibling');

// use the newly added ID to access that element
document.querySelector('#heading-sibling').style.backgroundColor = 'red';
```

### Accessing an Element's Classes <a href="#accessing-an-element-s-classes" id="accessing-an-element-s-classes"></a>

為了遵守Separation of concern，javascript最好不要管理style，那是css的工作。那javascript要幹嘛？

利用javascript找element，然後賦予已經在css定義好的class。

#### 1. The `.className` Property <a href="#the-classlist-property" id="the-classlist-property"></a>

如何顯示一個element所有的class？

```javascript
<h1 id="main-heading" class="ank-student jpk-modal">Learn Web Development at Udacity</h1>
```

We could use `.className` to access the list of classes:

```javascript
const mainHeading = document.querySelector('#main-heading');

// store the list of classes in a variable
const listOfClasses = mainHeading.className;

// logs out the string "ank-student jpk-modal"
console.log(listOfClasses);
```

The `.className` property returns a space-separated string of the classes. This isn't the most ideal format, unfortunately. We can, however, convert this space-separated string into an array using the JavaScript string method, `.split()`:

```javascript
const arrayOfClasses = listOfClasses.split(' ');

// logs out the array of strings ["ank-student", "jpk-modal"]
console.log(arrayOfClasses);
```

有了class list之後，就可以做以下操作：

* use a `for` loop to loop through the list of class names
* use `.push()` to add an item to the list
* use `.pop()` to remove an item from the list

設定class：

```javascript
mainHeading.className = "im-the-new-class";
```

The above code ***erases*** any classes that were **originally** in the element's `class` attribute and replaces it with the single class `im-the-new-class`.

#### 2. The `.classList` Property <a href="#the-classlist-property" id="the-classlist-property"></a>

Since `.className` returns a string, it makes it hard to add or remove individual classes. As I mentioned earlier, we can convert the string to an array and then use different Array Methods to search for a class remove it from the list, and then update the `.className` with the remaining classes. However, we don't want to do all of that work! Let's use the newer `.classList` property.

```javascript
<h1 id="main-heading" class="ank-student jpk-modal">Learn Web Development at Udacity</h1>
```

```javascript
const mainHeading = document.querySelector('#main-heading');

// store the list of classes in a variable
const listOfClasses = mainHeading.classList;

// logs out ["ank-student", "jpk-modal"]
console.log(listOfClasses);
```

Check out the documentation page on MDN: [classList docs](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)

The `.classList` property has a number of properties of its own. Some of the most popularly used ones are:

* `.add()` - to add a class to the list
* `.remove()` - to remove a class from the list
* `.toggle()` - to add the class if it doesn't exists or remove it from the list if it does already exist
* `.contains()` - returns a boolean based on if the class exists in the list or not

![](/files/-M6uKg1hxxea0RiOzd68)

最後說明一件事：為什麼 import js file都要擺在最後面？

可能因為html還沒有paint，dom還沒形成，所以js找不到element，無法做修改。

### Style Page Content Recap <a href="#style-page-content-recap" id="style-page-content-recap"></a>

We learned a ton of content in this section! We looked at:

* modifying individual styles with `.style.<prop>`
* updating multiple styles at once with `.style.cssText`
* getting/setting a list of classes with `.className`
* getting/setting/toggling CSS classes with `.classList`

My recommendation to you is that, out of the list of techniques you learned in this section, to use the `.classList` property more than any other. `.classList` is by far the most helpful property of the bunch, and it helps to keep your CSS styling out of your JavaScript code.
