Skip to content

Known Issues: @click.self and Click Event Handling

Last updated: 2026-04-27

Background

PR #716 ("Analytics fixes", McSneaky, merged into v6.8.2) changed multiple click handlers from @click to @click.self and removed .stop modifiers across the codebase. The intent was to make background clicks opt-in instead of opt-out, avoiding unintended bg click triggers from child elements.

This introduced two bugs that are still present in production (v6.8.5).

Bug 1: Clickable Background Broken by Visual Elements

Status: Broken since v6.8.2. Reported by Gediminas 2026-04-27.

What: Creatives with clickable background enabled no longer click through when a visual element (graphic, text, HTML block) covers the background area.

Root cause: CreativeBody.vue line 16 uses @click.self="handleBgClick". The .self modifier only fires when event.target === event.currentTarget (the body element itself). When a graphic block sits on top of the body, the click target is the graphic element, not the body, so .self never matches and handleBgClick never runs.

Before PR #716: @click="handleBgClick" -- clicks from child elements bubbled up and triggered handleBgClick. Visual elements didn't block background clicks.

After PR #716: @click.self="handleBgClick" -- only direct clicks on the body element itself trigger handleBgClick. Any child element absorbs the click.

Workaround: Add pointer-events: none in Custom CSS on the graphic block. This lets clicks pass through to the body.

Previous fix attempts:

  • PR #692 ("Fix blocks blocking bg click") -- added dynamic pointer-events based on hasClickthrough. This was before PR #716 changed to .self.
  • PR #711 ("Fix hover on visual elements") -- reverted #692 because pointer-events: none broke hover animations on visual elements. Set all visual elements back to pointer-events: auto.

Why PR #692/#711 are no longer the core issue: Those PRs tried to fix clickthrough by toggling pointer-events on visual elements. But the real problem is now @click.self on CreativeBody (introduced later in PR #716). Even with pointer-events: auto on visual elements, if CreativeBody used @click instead of @click.self, clicks would bubble through and trigger handleBgClick.

Bug 2: clicks Counter Effectively Always Zero

Status: Broken since v6.8.2. Not yet reported externally.

What: The clicks Bunny metric (raw click count on the creative) is almost never incremented.

Root cause: Creative.vue line 16 uses @click.self="creativeContainerClicked". CreativeBody covers the entire #creative-container, so event.target is never the container itself. The .self modifier prevents the handler from firing.

Impact: clicks in Bunny analytics is near-zero for all creatives. This was the "broadest click metric" intended to count every click anywhere. Currently useless.

Note: creativeClickedOnce (first interaction) was fixed in PR #735 by adding a separate @click.capture="handleFirstInteraction" handler. But clicks++ was left inside the .self handler and remains broken.

PR History

PRWhatEffect
#692Dynamic pointer-events on visual elementsFixed bg click passthrough, but broke hover animations
#711Reverted #692, back to pointer-events: autoRestored hover, bg click still worked (pre-#716)
#716Changed @click to @click.self on CreativeBody and Creative.vue, removed .stop modifiersBroke clickable background AND clicks counter
#735Added @click.capture for creativeClickedOnceFixed first-interaction detection, but clicks still broken

The .self Debate (PR #716 Review)

In the PR #716 review, Nicolay proposed using capture-phase counting instead of removing .stop everywhere:

js
// Capture phase listener fires before any .stop can block it
this.$el.addEventListener('click', (e) => {
  if (!e.isTrusted) return
  Analytics.clicks++
}, { capture: true })

Kevin went with .self instead, dismissing concerns from both Nicolay and Copilot about bubbling issues with "Nope, doesn't cause issues" / "Nope, no double data". He tested with creatives where visual elements didn't fully cover the background, so the issues weren't visible.

In PR #735 review (2026-04-23), Kevin acknowledged the .self issue for the clicks counter: "I didn't notice anything broken with current click.self, but now when you point it out, it might be good to test it over again."

Proposed Fixes

clicks counter (same fix regardless of approach)

Move clicks++ to a capture-phase handler, same pattern as handleFirstInteraction in PR #735:

js
@click.capture="handleAnyClick"

handleAnyClick(event: MouseEvent) {
  if (!event.isTrusted) return
  Analytics.clicks++
}

Counts every real click regardless of .stop or .self. Kevin already approved this pattern in PR #735.

Clickable background: three approaches

Approach A: Remove .self, use .stop on interactive elements

Revert CreativeBody to @click="handleBgClick". Clicks bubble naturally. Visual elements without clickthrough do nothing in handleClick(), so the click bubbles to body and triggers bg click. Visual elements WITH clickthrough call event.stopPropagation() to prevent double-firing. Interactive elements (choices, form, close button, etc.) get .stop back.

js
// VisualElementMixin.ts
handleClick(event: MouseEvent): void {
  if (!this.clickthroughUrl) return   // bubbles to handleBgClick
  event.stopPropagation()              // has own clickthrough, block bg click
  this.trackClick()
}
html
<!-- CreativeBody.vue -->
@click="handleBgClick"

<!-- Interactive elements get .stop back -->
<Choice @click.stop="..." />
<FormSubmitButton @click.stop="..." />
SummaryStandard DOM event model. Clicks bubble up by default, interactive elements opt out with .stop.
ProsCorrect event model. No pointer-events hacks. Hover animations preserved. Visual elements just work -- no special wiring needed.
ConsKevin opposed this in PR #716 ("devs must remember .stop"). New interactive components must add .stop or they trigger bg click.
ComplexitySmall: revert .self, restore .stop on ~6 interactive components, add event param to handleClick.
RiskLow. This is how it worked before PR #716. The .stop list is known and stable.

Approach B: Keep .self, emit bgClick from visual elements

Keep Kevin's .self on CreativeBody. Visual elements without clickthrough manually emit bgClick upward. CreativeBody already listens for @bg-click on conversation, video, and form blocks -- extend this to visual elements.

js
// VisualElementMixin.ts
handleClick(): void {
  if (this.clickthroughUrl) {
    this.trackClick()
    return
  }
  this.$parent?.$emit('bgClick')
}
html
<!-- CreativeBody.vue: add @bg-click to visual element templates -->
<CreativeGraphicBlock @bg-click="handleBgClick" ... />
<CreativeTextBlock @bg-click="handleBgClick" ... />
SummaryWorks within .self constraint. Visual elements explicitly forward clicks to bg handler via $emit, same pattern conversation/video/form already use.
ProsNo changes to .self or .stop. Kevin's model stays intact. Hover animations preserved. Consistent with existing @bg-click pattern already used by 3 block types.
ConsRequires adding @bg-click listeners in CreativeBody for visual element types. Slightly more wiring than Approach A.
ComplexitySmall-medium: modify handleClick to $emit('bgClick'), add @bg-click="handleBgClick" on graphic/text/HTML blocks in CreativeBody template.
RiskLow. Uses the same $emit/@bg-click pattern that conversation, video, and form blocks already use successfully.

Approach C: Keep .self, dynamic pointer-events

Keep .self. Make visual elements set pointer-events: none when bg click is enabled and they have no own clickthrough. Clicks pass through to body, .self matches.

js
// In visual element styles() computed
'pointerEvents': (DataStore.creativeSettings.creativeDelivery.backgroundClickthrough
  && !this.clickthroughUrl) ? 'none' : 'auto'
SummaryCSS-level fix. Visual elements become transparent to clicks when bg click is active.
ProsDeclarative. No event forwarding. Works with .self.
ConsBreaks hover animations and CSS :hover states (tried and reverted in PR #692/#711). Users must use custom CSS for hover effects on affected elements.
ComplexitySmall: add one computed check to each visual element's styles.
RiskHigh. Already tried and reverted once. Hover is a real feature people use.

Recommendation

Both A and B are good options with low risk.

Approach A uses standard DOM semantics and is the simplest mental model -- clicks bubble unless stopped. It was the working model before PR #716.

Approach B works within the existing .self model and extends a pattern already established for conversation, video, and form blocks. No need to revert Kevin's changes.

Approach C should be avoided -- it was already tried and reverted due to broken hover animations.

Key Files

FileRole
Creative-Engine/src/components/creative/Creative.vueContainer click handlers (@click.self, @click.capture)
Creative-Engine/src/components/conversationflow/CreativeBody.vueBackground click handler (@click.self="handleBgClick")
Creative-Engine/src/mixins/VisualElementMixin.tsVisual element click handler (handleClick)
Creative-Engine/src/components/creative/VisualElements/CreativeGraphicBlock.vueGraphic block with pointerEvents: 'auto'
Creative-Engine/src/components/creative/VisualElements/CreativeTextBlock.vueSame pattern
Creative-Engine/src/components/creative/VisualElements/CreativeHtmlBlock.vueSame pattern

Internal documentation