diff --git a/docs/hippy-react/gesture.md b/docs/hippy-react/gesture.md
index 96912547880..d92d839fdaa 100644
--- a/docs/hippy-react/gesture.md
+++ b/docs/hippy-react/gesture.md
@@ -142,12 +142,15 @@ new Hippy({
## 事件捕获
-> 最低支持版本 2.11.2
+> 最低支持版本 2.11.5
[[事件捕获范例]](//github.com/Tencent/Hippy/tree/master/examples/hippy-react-demo/src/components/ListView)
-点击事件和触屏事件支持事件捕获,如需注册捕获阶段的事件处理函数,则应在目标元素事件名添加 `Capture` 后缀,如 `onClickCapture`、`onTouchDownCapture`。如果目标元素没有`Capture`
-事件处理函数,默认不开启捕获。事件捕获会有一定性能损耗,如非必要尽量不开启。
+点击事件和触屏事件支持事件捕获,如需注册捕获阶段的事件处理函数,则应在目标元素事件名添加 `Capture` 后缀,如 `onClickCapture`、`onTouchDownCapture`。
+
+Hippy为了做更好的性能优化,如果目标元素没有 `Capture` 事件处理函数,默认不开启捕获,全局冒泡配置 `bubbles: false` 不会影响捕获开启。事件捕获设计与 Web 标准一致,当在任意一个捕获函数内调用 `stopPropagation` 时,会同时阻止剩余的捕获阶段、目标节点阶段和冒泡阶段执行。
+
+!> 事件捕获会有一定性能损耗,如非必要尽量不开启。
例子如下:
@@ -159,14 +162,17 @@ render()
onClick={() => {
console.log("根节点 点击");
}}
- onClickCapture={() => console.log("根节点 捕获点击")}
+ onClickCapture={(event) => {
+ // 如果根节点调用 stopPropagation,则按钮2的 onClickCapture 和按钮1的 onClick 都不会触发
+ // event.stopPropagation();
+ console.log("根节点 捕获点击")
+ }}
>
{
// 点击按钮1不会触发根节点捕获点击
console.log("按钮1 点击")
}}
-
>
点击按钮1
diff --git a/examples/hippy-react-demo/src/components/ListView/index.jsx b/examples/hippy-react-demo/src/components/ListView/index.jsx
index f87b4052572..7b9bb3ec1ae 100644
--- a/examples/hippy-react-demo/src/components/ListView/index.jsx
+++ b/examples/hippy-react-demo/src/components/ListView/index.jsx
@@ -216,6 +216,14 @@ export default class ListExample extends React.Component {
}
return (
{
+ console.log('onClickCapture style outer', event.target.nodeId, event.currentTarget.nodeId);
+ }}
+ onTouchDown={(event) => {
+ // outer onTouchDown would not be called, because style1 invoked event.stopPropagation();
+ console.log('onTouchDown style outer', event.target.nodeId, event.currentTarget.nodeId);
+ return false;
+ }}
onClick={(event) => {
console.log('click style outer', event.target.nodeId, event.currentTarget.nodeId);
// return false means trigger bubble
@@ -252,12 +260,15 @@ export default class ListExample extends React.Component {
console.log('onTouchDown ListView', event.target.nodeId, event.currentTarget.nodeId);
}}
onClickCapture={(event) => {
+ // if calling capture event stopPropagation in one of node,
+ // all capture phase left, target phase and bubbling phase would stop.
+ // event.stopPropagation();
console.log('onClickCapture listview', event.target.nodeId, event.currentTarget.nodeId);
}}
onClick={(event) => {
console.log('click listview', event.target.nodeId, event.currentTarget.nodeId);
// return false means trigger bubble
- return false;
+ return true;
}}
bounces={true}
overScrollEnabled={true}
diff --git a/packages/hippy-react/src/events/dispatcher.ts b/packages/hippy-react/src/events/dispatcher.ts
index d562ad57af9..c0da641fa6f 100644
--- a/packages/hippy-react/src/events/dispatcher.ts
+++ b/packages/hippy-react/src/events/dispatcher.ts
@@ -141,10 +141,14 @@ function doCaptureAndBubbleLoop(originalEventName: string, nativeEvent: NativeEv
currentTarget: getElementFromFiber(nextNodeItem),
});
}
- nextNodeItem = nextNodeItem.return;
- while (nextNodeItem && !isHostComponent(nextNodeItem.tag)) {
- // only handle HostComponent
+ if (eventQueue.length === 0) {
+ nextNodeItem = null;
+ } else {
nextNodeItem = nextNodeItem.return;
+ while (nextNodeItem && !isHostComponent(nextNodeItem.tag)) {
+ // only handle HostComponent
+ nextNodeItem = nextNodeItem.return;
+ }
}
}
if (eventQueue.length > 0) {
@@ -156,8 +160,13 @@ function doCaptureAndBubbleLoop(originalEventName: string, nativeEvent: NativeEv
const { eventName, currentTarget: currentTargetNode, listener, isCapture } = listenerObj;
const syntheticEvent = new Event(eventName, currentTargetNode, targetNode);
Object.assign(syntheticEvent, nativeEvent);
+ // whether it is capture or bubbling event, returning false or calling stopPropagation would both stop phase
if (isCapture) {
listener(syntheticEvent);
+ // event bubbles flag has higher priority
+ if (!syntheticEvent.bubbles) {
+ isStopBubble = true;
+ }
} else {
isStopBubble = listener(syntheticEvent);
// If callback have no return, use global bubble config to set isStopBubble.