Menu
×
   ❮   
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE
     ❯   

Vue 生命週期鉤子

生命週期鉤子 在 Vue 中是組件生命週期中的特定階段,我們可以在這些階段添加代碼來執行操作。

生命週期鉤子

每當組件在其生命週期中到達新的階段時,都會運行特定的函數,我們可以在該函數中添加代碼。這樣的函數稱為生命週期鉤子,因為我們可以將我們的代碼“掛鉤”到該階段。

以下是組件擁有的所有生命週期鉤子

  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeUnmount
  8. unmounted
  9. errorCaptured
  10. renderTracked
  11. renderTriggered
  12. activated
  13. deactivated
  14. serverPrefetch

以下是這些生命週期鉤子的示例。


'beforeCreate' 鉤子

The beforeCreate 生命週期鉤子在組件初始化之前發生,因此這是 Vue 設置組件的數據、計算屬性、方法和事件監聽器之前。

The beforeCreate 鉤子可以用於例如設置全局事件監聽器,但我們應該避免嘗試從 beforeCreate 生命週期鉤子訪問屬於組件的元素,例如數據、監控器和方法,因為它們在這個階段尚未創建。

此外,嘗試從 beforeCreate 生命週期鉤子訪問 DOM 元素也沒有意義,因為它們直到組件 mounted 後才會創建。

示例

CompOne.vue:

<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  beforeCreate() {
		this.text = 'initial text'; // This line has no effect
    console.log("beforeCreate: The component is not created yet.");
  }
}
</script>

App.vue:

<template>
  <h1>The 'beforeCreate' Lifecycle Hook</h1>
  <p>We can see the console.log() message from 'beforeCreate' lifecycle hook, but there is no effect from the text change we try to do to the Vue data property, because the Vue data property is not created yet.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

在上面的示例中,CompOne.vue 中的第 15 行沒有效果,因為在該行中我們嘗試更改 Vue 數據屬性中的文本,但 Vue 數據屬性實際上還沒有創建。此外,請記住打開瀏覽器控制台以查看 console.log() 在第 16 行的調用結果。


'created' 鉤子

The created 生命週期鉤子在組件初始化後發生,因此 Vue 已經設置了組件的數據、計算屬性、方法和事件監聽器。

我們應該避免嘗試從 created 生命週期鉤子訪問 DOM 元素,因為 DOM 元素直到組件 mounted 後才能訪問。

The created 生命週期鉤子可以用於使用 HTTP 請求獲取數據,或設置初始數據值。就像下面的示例一樣,數據屬性“text”被賦予了一個初始值

示例

CompOne.vue:

<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  created() {
		this.text = 'initial text';
    console.log("created: The component just got created.");
  }
}
</script>

App.vue:

<template>
  <h1>The 'created' Lifecycle Hook</h1>
  <p>We can see the console.log() message from 'created' lifecycle hook, and the text change we try to do to the Vue data property works, because the Vue data property is already created at this stage.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

'beforeMount' 鉤子

The beforeMount 生命週期鉤子在組件 mounted 之前發生,因此在組件添加到 DOM 之前。

我們應該避免嘗試從 beforeMount 生命週期鉤子訪問 DOM 元素,因為 DOM 元素直到組件 mounted 後才能訪問。

下面的示例顯示我們還不能訪問組件中的 DOM 元素,CompOne.vue 中的第 11 行不起作用,並在瀏覽器控制台中生成錯誤

示例

CompOne.vue:

<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p ref="pEl" id="pEl">We try to access this text from the 'beforeMount' hook.</p>
</template>

<script>
export default {
  beforeMount() {
    console.log("beforeMount: This is just before the component is mounted.");
    this.$refs.pEl.innerHTML = "Hello World!"; // <-- We cannot reach the 'pEl' DOM element at this stage 
  }
}
</script>

App.vue:

<template>
  <h1>The 'beforeMount' Lifecycle Hook</h1>
  <p>We can see the console.log() message from the 'beforeMount' lifecycle hook, but the text change we try to do to the 'pEl' paragraph DOM element does not work, because the 'pEl' paragraph DOM element does not exist yet at this stage.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pEl {
  background-color: lightcoral;
  display: inline-block;
}
</style>
运行示例 »

'mounted' 鉤子

組件添加到 DOM 樹後,mounted() 函數立即被調用,我們可以將我們的代碼添加到該階段。

這是我們第一次可以執行與屬於組件的 DOM 元素相關的操作,例如使用 ref 屬性和 $refs 對象,就像我們在下面的第二個示例中所做的那樣。

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>Right after this component is added to the DOM, the mounted() function is called and we can add code to that mounted() function. In this example, an alert popup box appears after this component is mounted.</p>
  <p><strong>Note:</strong> The reason that the alert is visible before the component is visible is because the alert is called before the browser gets to render the component to the screen.</p>
</template>

<script>
export default {
  mounted() {
    alert("The component is mounted!");
  }
}
</script>

App.vue:

<template>
  <h1>The 'mounted' Lifecycle Hook</h1>
  <button @click="this.activeComp = !this.activeComp">Create component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

注意:mounted 階段在組件添加到 DOM 後發生,但在上面的示例中,alert() 在我們看到組件之前就可見。造成這種情況的原因是,組件首先被添加到 DOM,但在瀏覽器開始將組件渲染到屏幕之前,mounted 階段發生,並且 alert() 變為可見並暫停瀏覽器渲染組件。

下面是一個可能更有用的示例:在表單組件安裝後將光標置於輸入字段中,以便用戶可以立即開始輸入。

示例

CompOne.vue:

<template>
  <h2>Form Component</h2>
  <p>When this component is added to the DOM tree, the mounted() function is called, and we put the cursor inside the input element.</p>
  <form @submit.prevent>
    <label>
      <p>
        Name: <br>
        <input type="text" ref="inpName">
      </p>
    </label>
    <label>
      <p>
        Age: <br>
        <input type="number">
      </p>
    </label>
    <button>Submit</button>
  </form>
  <p>(This form does not work, it is only here to show the mounted lifecycle hook.)</p>
</template>

<script>
  export default {
    mounted() {
      this.$refs.inpName.focus();
    }
  }
</script>
运行示例 »

'beforeUpdate' 鉤子

The beforeUpdate 生命週期鉤子在我們的組件數據發生變化時被調用,但在更新渲染到屏幕之前。The beforeUpdate 生命週期鉤子在 updated 生命週期鉤子之前發生。

The beforeUpdate 鉤子的一個特殊之處是,我們可以在不觸發新更新的情況下對應用程序進行更改,從而避免了其他情況下的無限循環。這就是為什麼不要在 updated 生命週期鉤子中對應用程序進行更改的原因,因為使用該鉤子會創建一個無限循環。看看下面的第三個示例,以紅色表示。

示例

The beforeUpdate() function adds an <li> tag to the document to indicate that the beforeUpdate() function has run.

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>

App.vue:

<template>
  <h1>The 'beforeUpdate' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is 'updated' and the 'beforeUpdate' hook happens just before that.</p>
  <p>It is safe to modify our page in the 'beforeUpdate' hook like we do here, but if we modify our page in the 'updated' hook, we will generate an infinite loop.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <ol ref="divLog"></ol>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  beforeUpdate() {
    this.$refs.divLog.innerHTML += "<li>beforeUpdate: This happened just before the 'updated' hook.</li>";
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

'updated' Hook

The updated lifecycle hook is called after our component has updated its DOM tree.

示例

The updated() function writes a message with console.log(). This happens whenever the page is updated, which in this example is every time the component is added or removed.

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>

App.vue:

<template>
  <h1>The 'updated' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is updated and the updated() function is called. In this example we use console.log() in the updated() function that runs when our application is updated.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  updated() {
    console.log("The component is updated!");
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
运行示例 »

We can see the result in the browser console after clicking the "Add/Remove Component" button 10 times

console screenshot

Note: We must be careful not to modify the page itself when the updated lifecycle hook is called, because then the page will update again and again, creating an infinite loop.

Lets try and see what happens if we do exactly like the note above warn us against. Will the page update indefinitely?

示例

The updated() function adds text to a paragraph, which in turn updates the page again, and the function runs again and again in an infinite loop. Luckily, your browser will stop this loop eventually.

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>

App.vue:

<template>
  <h1>The 'updated' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is updated and the updated() function is called.</p>
  <p>The first change that causes the updated hook to be called is when we remove the component by clicking the button. When this happens, the update() function adds text to the last paragraph, which in turn updates the page again and again.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <div>{{ text }}</div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true,
      text: "Hello, "
    }
  },
  updated() {
    this.text += "hi, ";
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
运行示例 »

When running the code above locally on your machine in dev mode, the Chrome browser console warning looks like this

screenshot browser console warning

'beforeUnmount' Hook

The beforeUnmount lifecycle hook is called just before a component is removed from the DOM.

As we can see in the example below, we can still access component elements in the DOM in the beforeUnmount hook.

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p ref="pEl">Strawberries!</p>
</template>
  
<script>
export default {
  beforeUnmount() {
    alert("beforeUnmount: The text inside the p-tag is: " + this.$refs.pEl.innerHTML);
  }
}
</script>

App.vue:

<template>
  <h1>Lifecycle Hooks</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

'unmounted' Hook

The unmounted lifecycle hook is called after a component is removed from the DOM.

This hook can for example be used to remove event listeners or cancelling timers or intervals.

When a component is unmounted, the unmounted() function is called, and we can add our code to it

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>When this component is removed from the DOM tree, the unmounted() function is called and we can add code to that function. In this example we create an alert popup box when this component is removed.</p>
</template>

<script>
export default {
  unmounted() {
    alert("The component is removed (unmounted)!");
  }
}
</script>

App.vue:

<template>
  <h1>Lifecycle Hooks</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
运行示例 »

Note: The unmounted stage happens AFTER the the component is removed from the DOM, but in the example above the alert() is visible BEFORE the component disappears. The reason for this is that first the component is removed from the DOM, but before the browser gets to render the removal of the component to the screen, the unmounted stage happens and the alert() becomes visible and pauses the browser from visibly removing the component.


'errorCaptured' Hook

The errorCaptured lifecycle hook is called when an error happens in a child/descendant component.

This hook can be used for error handling, logging or to display the error to the user.

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>This is the component</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>

App.vue:

<template>
  <h1>The 'errorCaptured' Lifecycle Hook</h1>
  <p>Whenever there is an error in a child component, the errorCaptured() function is called on the parent.</p>
  <p>When the button inside the component is clicked, a method will run that tries to do changes to a $refs object that does not exist. This creates an error in the component that triggers the 'errorCaptured' lifecycle hook in the parent, and an alert box is displayed with information about the error.</p>
  <p>After clicking "Ok" in the alert box you can see the error in the browser console.</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured() {
    alert("An error occurred");
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

Information about the error can also be captured as arguments to the errorCaptured() function and these arguments are

  1. the error
  2. the component that triggered the error
  3. the error source type

In the example below these arguments are captured in the errorCaptured() function and written to the console

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>This is the component</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>

App.vue:

<template>
  <h1>The 'errorCaptured' Lifecycle Hook</h1>
  <p>Whenever there is an error in a child component, the errorCaptured() function is called on the parent.</p>
  <p>Open the browser console to see the captured error details.</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured(error,compInst,errorInfo) {
    console.log("error: ", error);
    console.log("compInst: ", compInst);
    console.log("errorInfo: ", errorInfo);
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
运行示例 »

'renderTracked' and 'renderTriggered' Lifecycle Hooks

The renderTracked hook runs when a render function is set to track, or monitor, a reactive component. The renderTracked hook usually runs when a reactive component is initialized.

The renderTriggered hook runs when such a tracked reactive component changes, and therefore triggers a new render, so that the screen gets updated with the latest changes.

A reactive component is a component that can change.

A render function is a function compiled by Vue that keeps track of reactive components. When a reactive component changes, the render function is triggered and re-renders the application to the screen.

The renderTracked and renderTriggered hooks are meant to be used in debugging, and are only available in development mode.

To see the alert() and console.log() from the renderTracked and renderTriggered hooks, you must copy the code in the example below to your computer and run the application in development mode.

示例

CompOne.vue:

<template>
  <h2>Component One</h2>
  <p>This is a component.</p>
  <button @click="counter++">Add One</button>
  <p>{{ counter }}</p>
</template>
  
<script>
export default {
  data() {
    return {
      counter: 0
    }
  },
  renderTracked(evt) {
    console.log("renderTracked: ",evt);
    alert("renderTracked");
  },
  renderTriggered(evt) {
    console.log("renderTriggered: ",evt)
    alert("renderTriggered");
  }
}
</script>

App.vue:

<template>
  <h1>The 'renderTracked' and 'renderTriggered' Lifecycle Hooks</h1>
  <p>The 'renderTracked' and 'renderTriggered' lifecycle hooks are used for debugging.</p>
  <p><mark>This example only works in development mode, so to see the hooks run, you must copy this code and run it on you own computer in development mode.</mark></p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

Note: The code in the example above is intended to be copied and run locally on your computer in development mode, because the renderTracked and renderTriggered hooks only works in development mode.


'activated' and 'deactivated' Lifecycle Hooks

As we can see above on this page, we have the mounted and unmounted lifecycle hooks for when a component is removed or added to the DOM.

The activated and deactivated lifecycle hooks are for when a cached dynamic component is added or removed, but not from the DOM. The <KeepAlive> tag is used in the example below to cache the dynamic component.

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>Below is a log with every time the 'mounted' or 'activated' hooks run.</p>
  <ol ref="olEl"></ol>
  <p>You can also see when these hooks run in the console.</p>
</template>
  
<script>
export default {
  mounted() {
    console.log("mounted");
    const liEl = document.createElement("li");
    liEl.innerHTML = "mounted";
    this.$refs.olEl.appendChild(liEl);
  },
  activated() {
    console.log("activated");
    const liEl = document.createElement("li");
    liEl.innerHTML = "activated";
    this.$refs.olEl.appendChild(liEl);
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>

App.vue:

<template>
  <h1>The 'activated' Lifecycle Hook</h1>
  <p>In this example for the 'activated' hook we check if the component is cached properly with <KeepAlive>.</p>
  <p>If the component is cached properly with <KeepAlive> we expect the 'mounted' hook to run once the first time the component is included (must be added to the DOM the first time), and we expect the 'activated' hook to run every time the component is included (also the first time).</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

Let's expand the example above to see how both the activated and deactivated hooks work. Let's also use the mounted and unmounted hooks so that we can see that the mounted hook runs once the first time the cached component is added, and that the unmounted hook never runs for the cached component.

示例

CompOne.vue:

<template>
  <h2>Component</h2>
  <p>Below is a log with every time the 'activated', 'deactivated', 'mounted' or 'unmounted' hooks run.</p>
  <ol ref="olEl"></ol>
  <p>You can also see when these hooks run in the console.</p>
</template>
  
<script>
export default {
  mounted() {
    this.logHook("mounted");
  },
  unmounted() {
    this.logHook("unmounted");
  },
  activated() {
    this.logHook("activated");
  },
  deactivated() {
    this.logHook("deactivated");
  },
  methods: {
    logHook(hookName) {
      console.log(hookName);
      const liEl = document.createElement("li");
      liEl.innerHTML = hookName;
      this.$refs.olEl.appendChild(liEl);
    }
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>

App.vue:

<template>
  <h1>The 'activated' and 'deactivated' Lifecycle Hooks</h1>
  <p>In this example for the 'activated' and 'deactivated' hooks we also see when and if the 'mounted' and 'unmounted' hooks are run.</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
运行示例 »

'serverPrefetch' Lifecycle Hook

The 'serverPrefetch' hook is only called during server-side rendering (SSR).

Explaining and creating an example for the 'serverPrefetch' hook would require a relatively long introduction and setup, and that is beyond the scope of this tutorial.


Vue Exercises

Test Yourself With Exercises

Exercise

The  lifecycle hook is called 
just before a component is removed from the DOM.

Start the Exercise



×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
[email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
[email protected]

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2024 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.