# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET 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?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
