Search code examples
javascriptevent-handlingdom-events

Prevent onclick and event listeners from firing while in element selection mode


// Add event listener to second button
document.getElementById('button2').addEventListener('click', () => {
  console.log('Button 2 clicked');
});

class ElementSelector {
  constructor() {
    this.selectorModeEnabled = false;
    this.selectedElement = null;
    this.selectElement = this.selectElement.bind(this);
    this.onSelectElement = (element) => {
      console.log('Selected element:', element);
    };
  }

  enableSelectorMode() {
    this.selectorModeEnabled = true;
    document.body.style.cursor = "crosshair";
    document.body.addEventListener("click", this.selectElement);
  }

  disableSelectorMode() {
    this.selectorModeEnabled = false;
    document.body.style.cursor = "default";
    document.body.removeEventListener("click", this.selectElement);
  }

  toggleSelectorMode() {
    if (this.selectorModeEnabled) {
      this.disableSelectorMode();
    } else {
      this.enableSelectorMode();
    }
  }

  selectElement(event) {
    event.preventDefault();
    event.stopPropagation();
    const element = event.target;
    this.selectedElement = element;
    this.onSelectElement(element);
  }
}

// Initialize and set up toggle button
const selector = new ElementSelector();
document.getElementById('toggleSelector').addEventListener('click', () => {
  selector.toggleSelectorMode();
});
<!DOCTYPE html>
<html>

<head>
  <title>Element Selector Issue</title>
</head>

<body>
  <button onclick="console.log('Button 1 clicked')">Button 1</button>
  <button id="button2">Button 2</button>
  <button id="toggleSelector">Toggle Selector Mode</button>

</body>

</html>

When I click a button like this while in selection mode:

<button onclick="console.log('Button 1 clicked')">Click</button>

The console.log still fires even though I'm using both event.preventDefault() and event.stopPropagation(). I even tried event.stopImmediatePropagation() but the original click handlers still execute. How can I completely prevent all click events from firing on elements while in selection mode? Expected behavior: When in selection mode, clicking an element should only trigger my selection logic, not any existing click handlers. Actual behavior: Original click handlers (both onclick attributes and addEventListener) still fire even with event.preventDefault() and stopPropagation(). Any help would be appreciated!


Solution

  • Use the capture: true option to make the document event listener take priority over the element event listeners.

    Note that there's a Catch-22 here: When the element selector is enabled, clicking on the "Toggle Select Mode" button no longer runs the toggle function. You might want to add a specific check for this in the selectElement method.

    // Add event listener to second button
    document.getElementById('button2').addEventListener('click', () => {
      console.log('Button 2 clicked');
    });
    
    class ElementSelector {
      constructor() {
        this.selectorModeEnabled = false;
        this.selectedElement = null;
        this.selectElement = this.selectElement.bind(this);
        this.onSelectElement = (element) => {
          console.log('Selected element:', element);
        };
      }
    
      enableSelectorMode() {
        this.selectorModeEnabled = true;
        document.body.style.cursor = "crosshair";
        document.body.addEventListener("click", this.selectElement, {capture: true});
      }
    
      disableSelectorMode() {
        this.selectorModeEnabled = false;
        document.body.style.cursor = "default";
        document.body.removeEventListener("click", this.selectElement);
      }
    
      toggleSelectorMode() {
        if (this.selectorModeEnabled) {
          this.disableSelectorMode();
        } else {
          this.enableSelectorMode();
        }
      }
    
      selectElement(event) {
        event.preventDefault();
        event.stopPropagation();
        const element = event.target;
        this.selectedElement = element;
        this.onSelectElement(element);
      }
    }
    
    // Initialize and set up toggle button
    const selector = new ElementSelector();
    document.getElementById('toggleSelector').addEventListener('click', () => {
      selector.toggleSelectorMode();
    });
    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Element Selector Issue</title>
    </head>
    
    <body>
      <button onclick="console.log('Button 1 clicked')">Button 1</button>
      <button id="button2">Button 2</button>
      <button id="toggleSelector">Toggle Selector Mode</button>
    
    </body>
    
    </html>