diff --git a/ui/analyse/src/view/nvuiView.ts b/ui/analyse/src/view/nvuiView.ts index 4452a6f41e633..7ed610a0faa2f 100644 --- a/ui/analyse/src/view/nvuiView.ts +++ b/ui/analyse/src/view/nvuiView.ts @@ -147,7 +147,12 @@ export function renderNvui(ctx: AnalyseNvuiContext): VNode { hl('h2', i18n.site.board), hl( 'div.board', - { hook: { insert: el => boardEventsHook(ctx, el.elm as HTMLElement) } }, + { + hook: { + insert: el => boardEventsHook(ctx, el.elm as HTMLElement), + update: (_, vnode) => boardEventsHook(ctx, vnode.elm as HTMLElement), + }, + }, renderBoard( ctrl.chessground.state.pieces, ctrl.data.game.variant.key === 'racingKings' ? 'white' : ctrl.bottomColor(), @@ -224,13 +229,18 @@ function boardEventsHook( el: HTMLElement, ): void { const $board = $(el); - const $buttons = $board.find('button'); + // Remove old handlers before rebinding (important on re-render) + $board.off('.nvui'); const steps = () => ctrl.tree.getNodeList(ctrl.path); const fenSteps = () => steps().map(step => step.fen); const opponentColor = () => (ctrl.node.ply % 2 === 0 ? 'black' : 'white'); - $buttons.on('blur', leaveSquareHandler($buttons)); - $buttons.on('click', selectionHandler(opponentColor)); - $buttons.on('keydown', (e: KeyboardEvent) => { + $board.on('blur', 'button', e => { + leaveSquareHandler($board.find('button'))(e); + }); + $board.on('click', 'button', e => { + selectionHandler(opponentColor)(e); + }); + $board.on('keydown', 'button', (e: KeyboardEvent) => { if (e.shiftKey && e.key.match(/^[ad]$/i)) jumpMoveOrLine(ctrl)(e); else if (e.key.match(/^x$/i)) scanDirectionsHandler(ctrl.bottomColor(), ctrl.chessground.state.pieces, moveStyle.get())(e); diff --git a/ui/puzzle/src/view/nvuiView.ts b/ui/puzzle/src/view/nvuiView.ts index 5f6fd75fc59c6..983e342cb3195 100644 --- a/ui/puzzle/src/view/nvuiView.ts +++ b/ui/puzzle/src/view/nvuiView.ts @@ -25,15 +25,8 @@ const selectSound = throttled('select'); const borderSound = throttled('outOfBound'); const errorSound = throttled('error'); -export function renderNvui({ - ctrl, - notify, - moveStyle, - pieceStyle, - prefixStyle, - positionStyle, - boardStyle, -}: PuzzleNvuiContext): VNode { +export function renderNvui(ctx: PuzzleNvuiContext): VNode { + const { ctrl, notify, moveStyle, pieceStyle, prefixStyle, positionStyle, boardStyle } = ctx; notify.redraw = ctrl.redraw; const ground = ctrl.ground() || @@ -105,20 +98,8 @@ export function renderNvui({ 'div.board', { hook: { - insert: el => - boardEventsHook( - { - ctrl, - notify, - moveStyle, - pieceStyle, - prefixStyle, - positionStyle, - boardStyle, - }, - ground, - el.elm as HTMLElement, - ), + insert: el => boardEventsHook(ctx, ground, el.elm as HTMLElement), + update: (_, vnode) => boardEventsHook(ctx, ground, vnode.elm as HTMLElement), }, }, @@ -171,16 +152,14 @@ export function renderNvui({ function boardEventsHook(ctx: PuzzleNvuiContext, ground: Api, el: HTMLElement): void { const { ctrl, moveStyle, pieceStyle, prefixStyle, notify } = ctx; const $board = $(el); - const $buttons = $board.find('button'); + // Remove old handlers before rebinding (important on re-render) + $board.off('.nvui'); const steps = ctrl.tree.getNodeList(ctrl.path); const fenSteps = () => steps.map(step => step.fen); - $buttons.on('blur', nv.leaveSquareHandler($buttons)); - $buttons.on( - 'click', - nv.selectionHandler(() => opposite(ctrl.pov)), - ); - $buttons.on('keydown', (e: KeyboardEvent) => { + $board.on('blur', 'button', e => nv.leaveSquareHandler($board.find('button'))(e)); + $board.on('click', 'button', e => nv.selectionHandler(() => opposite(ctrl.pov))(e)); + $board.on('keydown', 'button', (e: KeyboardEvent) => { if (e.shiftKey && e.key.match(/^[ad]$/i)) nextOrPrev(ctrl)(e); else if (e.key.match(/^x$/i)) scanDirectionsHandler(