Why won't my Svelte dropdown close when the dropdown is open, but I click on the Dropdown Box?

104 Views Asked by At

What Works

Hi, I have an issue with my custom HTML dropdown made in Svelte. I'm a React engineer and new to svelte due to this company code that this small portion was written in just because previous from previous devs.

The initial click to open, handleSelection, and clickOutside, work.

What Doesn't Work

When the dropdown is open, the user should be able to click the dropdown box (class="select") and the dropdown should close. It doesn't close. Thanks in advance!

inputSelect.svelte

<svelte:options tag="input-select" />

<script lang="ts">
  import { onMount } from "svelte";
  import { get_current_component } from "svelte/internal";
  import Button from "./button.svelte";

  export let options = [];
  let selectedOption = null;
  let isOpen = false;
  let parent: HTMLElement;

  const component = get_current_component();

  function openDropdown(event: MouseEvent): void {
    console.log("openDropdown");
    isOpen = !isOpen;
    console.log("");
  }

  function handleSelection(value: string, event: Event): void {
    console.log("HANDLE SELECTION");
    selectedOption = value;

    component?.dispatchEvent(
      new CustomEvent("selectedOptionChanged", { detail: value })
    );

    console.log("HANDLE SELECTION");
    console.log("");
  }

  function clickOutside(node: HTMLElement) {
    const handleClick = (event: MouseEvent) => {
      if (
        node &&
        !node.contains(event.target as Node) &&
        isOpen === true
      ) {
        console.log("CLICK OUTSIDE");
        isOpen = false;
        console.log("");
      }
    };

    document.addEventListener("click", handleClick, true);

    return {
      destroy() {
        document.removeEventListener("click", handleClick, true);
      },
    };
  }

  onMount(() => {
    clickOutside(parent);
  });
</script>

<div>
  <div
    class="custom-select"
    style="border-color: {isOpen ? '#1677ff' : 'rgba(0, 0, 0, 0.25)'};"
  >
    <label
      for="entity-type"
      style="display: block; font-weight: 1000; margin-bottom: 4px;"
    >
      Entity Type {isOpen}
    </label>

    <div>
      <div
        class="select"
        class:select-arrow-active={isOpen}
        on:click={openDropdown}
        use:clickOutside
        bind:this={parent}
      >
        {#if selectedOption === null}
          <div>Select</div>
        {:else}
          <div>
            {options.find(
              (option) => option.value === selectedOption
            )?.label}
          </div>
        {/if}

        <div
          class="select-items"
          style={isOpen ? "display: block;" : "display: none;"}
        >
          {#each options as option (option.value)}
            <div
              on:click={(event) => {
                handleSelection(option.value, event);
                event.stopPropagation();
              }}
            >
              {option.label}
            </div>
          {/each}
        </div>
      </div>
    </div>
  </div>
</div>

<style lang="scss">
    .custom-select {
        position: relative;
        width: 200px;

        .select {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 8px 16px;
            border: 1px solid rgba(0, 0, 0, 0.25);
            cursor: pointer;
            user-select: none;
            border-radius: 4px;
            position: relative;

            &:after {
                content: "";
                width: 0;
                height: 0;
                border-style: solid;
                border-width: 4px 4px 0 4px;
                border-color: rgba(0, 0, 0, 0.25) transparent transparent
                    transparent;
                position: relative;
                transform: translateY(-50%) rotate(-180deg);
                margin-left: 8px;
                transition: transform 0.3s ease;
            }

            &.select-arrow-active::after,
            &.select-arrow-active::before {
                transform: translateY(-50%) rotate(0deg);
            }
        }

        .select-items {
            position: absolute;
            background-color: #fff;
            border: 1px solid #d9d9d9;
            border-radius: 4px;
            top: 100%;
            left: 0;
            right: 0;
            background-color: #ffffff;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
            z-index: 100;
        }

        .select-items div:hover {
            background-color: #bae0ff;
            color: #222222;
            font-weight: 600;
            font-size: 12px;
        }

        .select-items div {
            padding: 8px 15px;
            cursor: pointer;
        }

        .select-items div:first-child {
            border-top-left-radius: 4px;
            border-top-right-radius: 4px;
        }

        .select-items div:last-child {
            border-bottom-left-radius: 4px;
            border-bottom-right-radius: 4px;
        }

        .select-items div:first-child:hover {
            border-top-left-radius: 4px;
            border-top-right-radius: 4px;
        }

        .select-items div:last-child:hover {
            border-bottom-left-radius: 4px;
            border-bottom-right-radius: 4px;
        }
    }
</style>

What Logs To The Console

  1. Initial Click to open dropdown

openDropdown

  1. Dropdown is open, now I click the select box.
CLICK OUTSIDE

openDropdown
1

There are 1 best solutions below

0
Peppe L-G On

Seems like the problem is that openDropdown() is called also when you click "outside". I imagine an easy fix is to call event.stopPropagation() in handleClick() (inside the if).