Skip to content

docs(Advanced guides): Translate Portals #199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 32 additions & 34 deletions content/docs/portals.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
---
id: portals
title: Portals
title: Portal
permalink: docs/portals.html
---

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
Portal 提供一個優秀方法來讓 children 可以 render 到 parent component DOM 樹以外的 DOM 節點。

```js
ReactDOM.createPortal(child, container)
```

The first argument (`child`) is any [renderable React child](/docs/react-component.html#render), such as an element, string, or fragment. The second argument (`container`) is a DOM element.
第一個參數(`child`)是任何[可 render 的 React child](/docs/react-component.html#render),例如 elementstring 或者 fragment。第二個參數(`container`)則是一個 DOM element

## Usage {#usage}
## 使用方式 {#usage}

Normally, when you return an element from a component's render method, it's mounted into the DOM as a child of the nearest parent node:
通常當 component 的 render 方法回傳一個 element 時,此 element 會作為 child 被 mount 進最接近的 parent 節點中:

```js{4,6}
render() {
// React mounts a new div and renders the children into it
// React mount 一個新的 div 並將 children render 進去
return (
<div>
{this.props.children}
Expand All @@ -27,34 +27,34 @@ render() {
}
```

However, sometimes it's useful to insert a child into a different location in the DOM:
然而在某些狀況下,將 child 插入不同位置的 DOM 內十分好用:

```js{6}
render() {
// React does *not* create a new div. It renders the children into `domNode`.
// `domNode` is any valid DOM node, regardless of its location in the DOM.
// React *不會*建立新的 div。它會將 children render 進 `domNode` 中。
// `domNode` 可以是任何在隨意位置的合法 DOM node
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
```

A typical use case for portals is when a parent component has an `overflow: hidden` or `z-index` style, but you need the child to visually "break out" of its container. For example, dialogs, hovercards, and tooltips.
一個典型的 portal 使用案例是,當 parent component `overflow: hidden` 或者 `z-index` 的樣式時,卻仍需要 child 在視覺上「跳出」其容器的狀況。例如 dialog、hovercard 與 tooltip 都屬於此案例。

> Note:
> 注意:
>
> When working with portals, remember that [managing keyboard focus](/docs/accessibility.html#programmatically-managing-focus) becomes very important.
> 當使用 portal 時,請留意[控管鍵盤 focus](/docs/accessibility.html#programmatically-managing-focus) 對於無障礙功能會變得非常重要。
>
> For modal dialogs, ensure that everyone can interact with them by following the [WAI-ARIA Modal Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal).
> 使用跳窗 dialog 時,應確保每個人都可以依照 [WAI-ARIA Modal 開發規範](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal) 定義的方式與其互動。

[**Try it on CodePen**](https://codepen.io/gaearon/pen/yzMaBd)
[**CodePen 上試試看吧!**](https://codepen.io/gaearon/pen/yzMaBd)

## Event Bubbling Through Portals {#event-bubbling-through-portals}
## 透過 Portal 進行 Event Bubbling {#event-bubbling-through-portals}

Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every other way. Features like context work exactly the same regardless of whether the child is a portal, as the portal still exists in the *React tree* regardless of position in the *DOM tree*.
雖然 portal 可以被放置在 DOM tree 中的任何位置,但 portal 的其他行為與一般的 React child 別無二致。像是 context 等功能的運作方式並不會因為 child portal 而有所不同,因為不論 portal 在 DOM tree 中的位置為何,它都存在於 *React tree* 中。

This includes event bubbling. An event fired from inside a portal will propagate to ancestors in the containing *React tree*, even if those elements are not ancestors in the *DOM tree*. Assuming the following HTML structure:
相同的行為也包括 event bubbling。一個在 portal 內觸發的 event 會傳遞到涵蓋它的 *React tree* 祖先中,就算涵蓋它的那些 element 並不是 DOM tree 上的祖先。假設存在以下 HTML 結構:

```html
<html>
Expand All @@ -65,10 +65,10 @@ This includes event bubbling. An event fired from inside a portal will propagate
</html>
```

A `Parent` component in `#app-root` would be able to catch an uncaught, bubbling event from the sibling node `#modal-root`.
一個在 `#app-root` 中的 `Parent` component 可以捕捉從 sibling 節點 `#modal-root` bubble 上來且未被接收過的 event。

```js{28-31,42-49,53,61-63,70-71,74}
// These two containers are siblings in the DOM
// 這兩個 container 是 DOM 上的 sibling
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

Expand All @@ -79,14 +79,13 @@ class Modal extends React.Component {
}

componentDidMount() {
// The portal element is inserted in the DOM tree after
// the Modal's children are mounted, meaning that children
// will be mounted on a detached DOM node. If a child
// component requires to be attached to the DOM tree
// immediately when mounted, for example to measure a
// DOM node, or uses 'autoFocus' in a descendant, add
// state to Modal and only render the children when Modal
// is inserted in the DOM tree.
// Portal element 會在 Modal 的 children 被
// mount 之後才插入 DOM tree 中,這代表 children
// 會被 mount 在一個分離的 DOM 節點上。如果一個
// child component 需要在 mount 結束時馬上連接到 DOM tree 中,
// 例如測量一個 DOM node,或者在子節點中使用 'autoFocus' 等狀況,
// 則應將 state 加入 Modal 中,並只在 Modal 插入 DOM tree 後
// 才 render children。
modalRoot.appendChild(this.el);
}

Expand All @@ -110,9 +109,8 @@ class Parent extends React.Component {
}

handleClick() {
// This will fire when the button in Child is clicked,
// updating Parent's state, even though button
// is not direct descendant in the DOM.
// 這會在 Child 中的 button 被點擊時觸發並更新 Parent 的 state,
// 就算 Child 的 button 並不是 DOM 中的直接後代。
this.setState(state => ({
clicks: state.clicks + 1
}));
Expand All @@ -137,8 +135,8 @@ class Parent extends React.Component {
}

function Child() {
// The click event on this button will bubble up to parent,
// because there is no 'onClick' attribute defined
// 這個 button 中的 click event 會被 bubble parent 中,
// 因為這邊並沒有定義 'onClick' attribute
return (
<div className="modal">
<button>Click</button>
Expand All @@ -149,6 +147,6 @@ function Child() {
ReactDOM.render(<Parent />, appRoot);
```

[**Try it on CodePen**](https://codepen.io/gaearon/pen/jGBWpE)
[**CodePen 上試試看吧!**](https://codepen.io/gaearon/pen/jGBWpE)

Catching an event bubbling up from a portal in a parent component allows the development of more flexible abstractions that are not inherently reliant on portals. For example, if you render a `<Modal />` component, the parent can capture its events regardless of whether it's implemented using portals.
Parent component 可以捕捉從 portal bubble 上來的 event 能使開發具有不直接依賴於 portal 的更彈性化抽象性。舉例來說,如果你 render 了一個 `<Modal />` component,則不論 Modal 是否是使用 portal 實作,它的 parent 皆可以捕捉到其 event。