Vue에서 요소 외부 클릭 감지

Vue Core와 함께 제공되는 v-model , v-if , v-for 또는 v-show를 사용했을 수 있습니다. 이러한 유틸리티를 지시문이라고 하며 DOM 요소에 연결할 수 있는 작은 명령입니다.

뷰 지시문



앱을 빌드할 때 Vue가 제공해야 하는 코드 재사용 및 추상화의 기본 형태는 구성 요소입니다. 그러나 일반 요소에 대한 저수준 DOM 액세스가 필요한 경우가 있을 수 있으며 여기에서 사용자 지정 지시문을 사용해야 합니다. 지시문은 DOM 조작만을 캡슐화하기 위한 것이며 구성 요소는 자체 보기 및 데이터 논리가 있는 자체 포함된 단위라는 점에 유의하는 것이 중요합니다.

이러한 사용 사례의 한 가지 좋은 예는 툴팁입니다. v-tooltip 은 인기 있는 라이브러리이고 popperjs 에 대한 래퍼입니다. 이 라이브러리는 지시어로 등록할 수 있으며 다음과 같이 사용할 수 있습니다.

<button v-tooltip="'You have ' + count + ' new messages.'">


며칠 전 저는 deepsource.io 에서 드롭다운 메뉴 구성 요소를 만드는 구성 요소 시스템 작업을 하고 있었습니다. 요소 외부를 클릭할 때마다 드롭다운 메뉴가 닫히기를 원했습니다. 이것은 사용자 지정 지시문을 사용할 수 있는 이상적인 시나리오입니다.

이것을 지시문으로 빌드하면 모달 구성 요소와 같이 원하는 곳 어디에서나 이 기능을 재사용할 수 있습니다.

사용자 지정 지시문 작성



Vue는 요소를 렌더링하는 특정 단계에서 트리거되는 포괄적인 후크 제품군을 제공합니다. 후크는 다음과 같습니다.
  • bind – 지시문이 요소에 첨부되면 이 문제가 발생합니다. 이것을 init 함수
  • 처럼 생각하십시오.
  • inserted – 이 후크는 요소가 부모 DOM에 삽입되면 발생합니다.
  • update – 이 후크는 요소가 업데이트될 때 호출되지만 자식은 아직 업데이트되지 않았습니다.
  • componentUpdated – 이 후크는 구성 요소와 자식이 업데이트되면 호출됩니다.
  • unbind – 이 후크는 지시문이 제거되면 호출됩니다.

  • Vue 문서에는 입력 구성 요소에 대한 동작과 유사한 v-focus autofocus 지시문이 있습니다. 확인하실 수 있습니다here.

    빌드할 디렉티브로 이동해 보겠습니다.

    외부 클릭 지시문



    장난감 드롭다운 구성 요소부터 시작하겠습니다.


    <template>
      <button v-on:click="toggle" class="dropdown-button">Menu</button>
      <div v-if="isOpen" v-outside-click="close" class="dropdown-body">
        <ul>
          <li>Account Settings</li>
          <li>Notifications</li>
          <li>Log Out</li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      name: 'ToyDropdown'
      data: {
        return {isOpen: false}
      },
      methods: {
        toggle() {
          this.isOpen = !this.isOpen
        },
        close() {
          this.isOpen = false
        }
      }
    }
    </script>
    



    여기서 우리는 바인딩을 생성한 요소 외부를 클릭했을 때 close 함수가 트리거되기를 원합니다. 정확히 그렇게 하는 함수로 시작하겠습니다.

    function onDocumentClick(e, el, fn) {
      let target = e.target;
      if (el !== target && !el.contains(target)) {
        fn(e);
      }
    }
    


    디렉티브를 만들어 봅시다. 이 경우 bindunbind 후크만 필요합니다.

    export default {
      bind(el, binding) {
        const fn = binding.value;
        const click = function (e) {
          onDocumentClick(e, el, fn);
        };
    
        document.addEventListener("click", click);
      },
      unbind(el) {
        // Remove event handler
      },
    };
    


    바인드는 다른 후크와 마찬가지로 몇 가지 인수를 받습니다. 모두 볼 수 있습니다here. 우리가 관심을 갖는 것은 binding 지시문 이름, 전달된 값 등을 포함하는 객체입니다.

    우리의 경우 값은 외부 클릭 시 트리거되는 함수입니다.

    이것만으로도 잘 작동하지만 unbind에서 이벤트 리스너를 제거해야 합니다. 즉, 나중에 참조할 수 있도록 추가된 이벤트 리스너를 메모리에 저장해야 합니다. 이것은 해결하기 간단합니다. 모든 이벤트 리스너를 저장할 배열만 있으면 됩니다. 또한 이벤트 리스너의 인덱스를 인식하기 위해 요소의 데이터 속성에 인덱스를 첨부합니다.

    지시문 기능은 이제 다음과 같습니다.

    const instance = [];
    
    export default {
      bind(el, binding) {
        // add the index to element data attributes
        el.dataset.outsideClickIndex = instances.length;
    
        const fn = binding.value;
        const click = function (e) {
          onDocumentClick(e, el, fn);
        };
    
        document.addEventListener("click", click);
        instances.push(click);
      },
      unbind(el) {
        // Remove event handler
      },
    };
    


    이제 outsideClickIndexinstances 배열을 사용하여 바인딩 해제 시 이벤트 리스너를 제거할 수 있습니다.

    unbind(el) {
        const index = el.dataset.outsideClickIndex;
        const handler = instances[index];
        document.removeEventListener('click', handler);
        instances.splice(index, 1);
    }
    


    우리가 할 수 있는 또 다른 개선 사항은 touchstart에 대한 이벤트도 추가하는 것입니다.

    이것으로 우리의 지시문은 다음과 같이 보입니다.


    let instances = [];
    
    function onDocumentClick(e, el, fn) {
      let target = e.target;
      if (el !== target && !el.contains(target)) {
        fn(e);
      }
    }
    
    export default {
      bind(el, binding) {
        el.dataset.outsideClickIndex = instances.length;
    
        const fn = binding.value;
        const click = function (e) {
          onDocumentClick(e, el, fn);
        };
    
        document.addEventListener("click", click);
        document.addEventListener("touchstart", click);
        instances.push(click);
      },
      unbind(el) {
        const index = el.dataset.outsideClickIndex;
        const handler = instances[index];
        document.removeEventListener("click", handler);
        document.removeEventListener("touchstart", click);
        instances.splice(index, 1);
      },
    };
    



    여기 있습니다. main.js 파일에서 다음과 같이 지시문을 등록할 수 있습니다.

    import outsideClickDirective from "../../directives/outside-click";
    Vue.directive("outside-click", outsideClickDirective);
    


    그게 다야.

    추신 이 게시물은 원래 내 블로그shivam.dev에 게시되었습니다.

    참고문헌


  • Custom Directives - Vue.js
  • 좋은 웹페이지 즐겨찾기