Izaan
Izaan

Reputation: 85

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!

Upvotes: 1

Views: 56

Answers (2)

Muhammad Saqib
Muhammad Saqib

Reputation: 388

There are two steps you need to do. If you want to prevent any other click in selection mode.

  • First Intercept the event before reach to target element using capture: true
  • Second stop the events propagation immediately using stopImmediatePropagation();

Here is the code which work on my local:

<!DOCTYPE html>
<html>
  <head>
    <title>Element Selector Issue</title>
  </head>
  <body>
    <div class="selection-area">
      <button onclick="console.log('Button 1 clicked')">Button 1</button>
      <button id="button2">Button 2</button>
    </div>

    <button id="toggleSelector">Toggle Selector Mode</button>

    <script>
      document.getElementById("button2").addEventListener("click", () => {
        console.log("Button 2 clicked");
      });

      class ElementSelector {
        constructor() {
          this.selectorModeEnabled = false;
          this.selectedElement = null;
          this.selectionArea = document.querySelector(".selection-area");
          this.selectElement = this.selectElement.bind(this);
          this.onSelectElement = element => {
            console.log("Selected element:", element);
          };
        }

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

        disableSelectorMode() {
          this.selectorModeEnabled = false;
          this.selectionArea.style.cursor = "default";
          console.log("-- Disabling Selection Mode --");
          this.selectionArea.removeEventListener(
            "click",
            this.selectElement,
            true
          );
        }

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

        selectElement(event) {
          if (!this.selectorModeEnabled) return;

          event.preventDefault();
          event.stopImmediatePropagation();
          console.log("-- preventing other clicks --");

          this.selectedElement = event.target;
          this.onSelectElement(event.target);
        }
      }

      const selector = new ElementSelector();
      document
        .getElementById("toggleSelector")
        .addEventListener("click", () => {
          selector.toggleSelectorMode();
        });
    </script>
  </body>
</html>

Also define your selection Area or container (what ever you prefer to call it). Because if you define document.body as selection area it's not gonna disable your selectionMode as the button for disabling the selectionMode is also in selection area and the acutal event is not gonna get fired.

Upvotes: 0

Barmar
Barmar

Reputation: 782158

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>

Upvotes: 1

Related Questions