ตรวจจับคลิกภายนอกองค์ประกอบ


121

ฉันจะตรวจจับการคลิกนอกองค์ประกอบของฉันได้อย่างไร ฉันใช้ Vue.js ดังนั้นมันจะอยู่นอกองค์ประกอบเทมเพลตของฉัน ฉันรู้วิธีทำใน Vanilla JS แต่ฉันไม่แน่ใจว่ามีวิธีที่เหมาะสมกว่านี้หรือไม่เมื่อฉันใช้ Vue.js?

นี่เป็นวิธีแก้ปัญหาสำหรับเหตุการณ์ Vanilla JS: Javascript Detect Click ภายนอก div

ฉันเดาว่าฉันสามารถใช้วิธีที่ดีกว่าในการเข้าถึงองค์ประกอบได้หรือไม่?


ส่วนประกอบ Vue ถูกแยกออก ดังนั้นการตรวจจับการเปลี่ยนแปลงภายนอกจึงไม่เป็นปัญหาและใช้รูปแบบการต่อต้าน
Raj Kamal

ขอบคุณ ฉันไม่แน่ใจว่าจะใช้งานในองค์ประกอบ Vue ได้อย่างไร ยังต้องมีแนวทางปฏิบัติที่ดีที่สุดสำหรับการต่อต้านรูปแบบ?

คอมโพเนนต์ Vue.js ถูกแยกออกซึ่งนั่นเป็นความจริง แต่มีวิธีการที่แตกต่างกันสำหรับการสื่อสารระหว่างแม่ลูก ดังนั้นแทนที่จะขอให้ตรวจจับเหตุการณ์ภายนอกองค์ประกอบคุณควรระบุว่าคุณต้องการตรวจจับองค์ประกอบภายในองค์ประกอบจากองค์ประกอบหลักจากลูกบางส่วนหรือความสัมพันธ์ใด ๆ ที่อยู่ระหว่างองค์ประกอบ
Yerko Palma

ขอบคุณสำหรับความคิดเห็น. คุณมีตัวอย่างหรือลิงค์ที่ฉันสามารถติดตามได้หรือไม่?

github.com/simplesmiler/vue-clickawayสามารถทำให้งานของคุณง่ายขึ้น
Raj Kamal

คำตอบ:


97

สามารถแก้ไขได้อย่างดีโดยตั้งค่าคำสั่งที่กำหนดเองเพียงครั้งเดียว:

Vue.directive('click-outside', {
  bind () {
      this.event = event => this.vm.$emit(this.expression, event)
      this.el.addEventListener('click', this.stopProp)
      document.body.addEventListener('click', this.event)
  },   
  unbind() {
    this.el.removeEventListener('click', this.stopProp)
    document.body.removeEventListener('click', this.event)
  },

  stopProp(event) { event.stopPropagation() }
})

การใช้งาน:

<div v-click-outside="nameOfCustomEventToCall">
  Some content
</div>

ในส่วนประกอบ:

events: {
  nameOfCustomEventToCall: function (event) {
    // do something - probably hide the dropdown menu / modal etc.
  }
}

การสาธิตการทำงานบน JSFiddle พร้อมข้อมูลเพิ่มเติมเกี่ยวกับข้อควรระวัง:

https://jsfiddle.net/Linusborg/yzm8t8jq/


3
ฉันใช้ vue clickaway แต่ฉันคิดว่าวิธีแก้ปัญหาของคุณไม่มากก็น้อยเหมือนกัน ขอบคุณ

56
วิธีนี้ใช้ไม่ได้อีกต่อไปใน Vue.js 2 การเรียก self.vm. $ emit ให้ข้อความแสดงข้อผิดพลาด
เหนือ

3
การใช้ @blur ก็เป็นอีกทางเลือกหนึ่งและทำให้การให้ผลลัพธ์แบบเดียวกันง่ายขึ้น: <input @ เบลอ = "hide"> โดยที่ hide: function () {this.isActive = false; }
รวบรวมข้อมูล

1
คำตอบควรได้รับการแก้ไขเพื่อระบุว่าฉันไม่ใช่สำหรับ Vue.js 1 เท่านั้น
Stéphane Gerber

167

มีวิธีแก้ปัญหาที่ฉันใช้ซึ่งอ้างอิงจากคำตอบของ Linus Borg และทำงานได้ดีกับ vue.js 2.0

Vue.directive('click-outside', {
  bind: function (el, binding, vnode) {
    el.clickOutsideEvent = function (event) {
      // here I check that click was outside the el and his children
      if (!(el == event.target || el.contains(event.target))) {
        // and if it did, call method provided in attribute value
        vnode.context[binding.expression](event);
      }
    };
    document.body.addEventListener('click', el.clickOutsideEvent)
  },
  unbind: function (el) {
    document.body.removeEventListener('click', el.clickOutsideEvent)
  },
});

คุณผูกเข้ากับมันโดยใช้v-click-outside:

<div v-click-outside="doStuff">

นี่คือตัวอย่างเล็ก ๆ

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับคำสั่งที่กำหนดเองและความหมายของel การผูก vnodeในhttps://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments


8
ใช้งานได้ แต่ในคำสั่ง Vue 2.0 ไม่มีอินสแตนซ์อีกต่อไปดังนั้นจึงไม่ได้กำหนดไว้ vuejs.org/v2/guide/migration.html#Custom-Directives-simplified ฉันไม่รู้ว่าทำไมซอถึงใช้งานได้หรือเมื่อการทำให้เข้าใจง่ายนี้เสร็จสิ้น (หากต้องการแก้ให้แทนที่ "this" ด้วย "el" เพื่อผูกเหตุการณ์กับองค์ประกอบ)
Busata

1
อาจเป็นเพราะหน้าต่างส่งผ่านเป็น "this" ฉันได้แก้ไขคำตอบแล้ว ขอขอบคุณที่ชี้ให้เห็นข้อบกพร่องนี้
MadisonTrash

8
มีวิธียกเว้นองค์ประกอบเฉพาะภายนอกหรือไม่? ตัวอย่างเช่นฉันมีปุ่มเดียวที่อยู่ด้านนอกซึ่งต้องเปิดองค์ประกอบนี้และเนื่องจากมันทำให้ทั้งสองวิธีไม่มีอะไรเกิดขึ้น
Žilvinas

5
คุณช่วยอธิบาย vnode.context [binding.expression] (เหตุการณ์); ?
Sainath SR

1
วิธีการเปลี่ยนสิ่งนี้เพื่อให้สามารถใช้นิพจน์แทนวิธีการภายใน v-click-outside ถูกทริกเกอร์?
raphadko

50

เพิ่มtabindexแอตทริบิวต์ให้กับส่วนประกอบของคุณเพื่อให้สามารถโฟกัสได้และทำสิ่งต่อไปนี้:

<template>
    <div
        @focus="handleFocus"
        @focusout="handleFocusOut"
        tabindex="0"
    >
      SOME CONTENT HERE
    </div>
</template>

<script>
export default {    
    methods: {
        handleFocus() {
            // do something here
        },
        handleFocusOut() {
            // do something here
        }
    }
}
</script>

4
โว้ว! ฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่สั้นและสะอาดที่สุด เป็นคนเดียวที่ใช้ได้ผลในกรณีของฉัน
Matt Komarnicki

3
เพียงเพิ่มเข้าไปในสิ่งนี้การตั้งค่าดัชนีแท็บเป็น -1 จะหยุดไม่ให้กล่องไฮไลต์ปรากฏขึ้นเมื่อคุณคลิกที่องค์ประกอบ แต่จะยังทำให้ div สามารถโฟกัสได้
Colin

1
ด้วยเหตุผลบางอย่าง tabindex ของ -1 ไม่ได้ซ่อนโครงร่างให้ฉันดังนั้นฉันจึงเพิ่มoutline: none;โฟกัสสำหรับองค์ประกอบ
Art3mix

1
เราจะนำสิ่งนี้ไปใช้กับการนำทางด้านข้างแบบไม่ใช้ผ้าใบที่เลื่อนลงบนหน้าจอได้อย่างไร ฉันไม่สามารถให้โฟกัส sidenav ได้เว้นแต่จะถูกคลิก
Charles Okwuagwu

1
ซึ่งเป็นวิธีที่ทรงพลังที่สุดอย่างแน่นอน ขอบคุณ! :)
Canet Robern

23

มีสองแพ็คเกจที่พร้อมใช้งานในชุมชนสำหรับงานนี้ (ทั้งสองอย่างได้รับการบำรุงรักษา):


8
vue-clickawayแพ็คเกจแก้ไขปัญหาของฉันได้อย่างสมบูรณ์แบบ ขอบคุณ
Abdalla Arbab

1
แล้วสิ่งของมากมายล่ะ? ทุกรายการที่มีเหตุการณ์คลิกภายนอกจะเริ่มเหตุการณ์ในแต่ละคลิก เป็นเรื่องดีเมื่อคุณสร้างบทสนทนาและแย่มากเมื่อคุณสร้างแกลเลอรี ในยุคที่ไม่มีองค์ประกอบเรากำลังฟังการคลิกจากเอกสารและตรวจสอบว่าองค์ประกอบใดถูกคลิก แต่ตอนนี้มันปวด.
br.

@Julien Le Coupanec ฉันพบว่าวิธีนี้ดีที่สุดแล้ว! ขอบคุณมากสำหรับการแบ่งปัน!
Manuel Abascal

7

สิ่งนี้ใช้ได้กับฉันด้วย Vue.js 2.5.2:

/**
 * Call a function when a click is detected outside of the
 * current DOM node ( AND its children )
 *
 * Example :
 *
 * <template>
 *   <div v-click-outside="onClickOutside">Hello</div>
 * </template>
 *
 * <script>
 * import clickOutside from '../../../../directives/clickOutside'
 * export default {
 *   directives: {
 *     clickOutside
 *   },
 *   data () {
 *     return {
         showDatePicker: false
 *     }
 *   },
 *   methods: {
 *     onClickOutside (event) {
 *       this.showDatePicker = false
 *     }
 *   }
 * }
 * </script>
 */
export default {
  bind: function (el, binding, vNode) {
    el.__vueClickOutside__ = event => {
      if (!el.contains(event.target)) {
        // call method provided in v-click-outside value
        vNode.context[binding.expression](event)
        event.stopPropagation()
      }
    }
    document.body.addEventListener('click', el.__vueClickOutside__)
  },
  unbind: function (el, binding, vNode) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null
  }
}

ขอบคุณสำหรับตัวอย่างนี้ ตรวจสอบสิ่งนี้ใน vue 2.6 มีการแก้ไขบางอย่างในวิธีการยกเลิกการผูกคุณต้องแก้ไขปัญหาบางอย่างโดยสิ่งนี้ (คุณลืมคุณสมบัติของร่างกายในวิธีการเลิกผูก): document.body.removeEventListener ('click', el .__ vueClickOutside__); ถ้าไม่ - จะทำให้เกิดการสร้างผู้ฟังเหตุการณ์หลายครั้งหลังจากการพักผ่อนหย่อนใจทุกองค์ประกอบ (รีเฟรชหน้า)
Alexey Shabramov

7
export default {
  bind: function (el, binding, vNode) {
    // Provided expression must evaluate to a function.
    if (typeof binding.value !== 'function') {
      const compName = vNode.context.name
      let warn = `[Vue-click-outside:] provided expression '${binding.expression}' is not a function, but has to be`
      if (compName) { warn += `Found in component '${compName}'` }

      console.warn(warn)
    }
    // Define Handler and cache it on the element
    const bubble = binding.modifiers.bubble
    const handler = (e) => {
      if (bubble || (!el.contains(e.target) && el !== e.target)) {
        binding.value(e)
      }
    }
    el.__vueClickOutside__ = handler

    // add Event Listeners
    document.addEventListener('click', handler)
  },

  unbind: function (el, binding) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null

  }
}

5

ฉันได้รวมคำตอบทั้งหมด (รวมถึงบรรทัดจาก vue-clickaway) และคิดหาวิธีแก้ปัญหานี้ที่เหมาะกับฉัน:

Vue.directive('click-outside', {
    bind(el, binding, vnode) {
        var vm = vnode.context;
        var callback = binding.value;

        el.clickOutsideEvent = function (event) {
            if (!(el == event.target || el.contains(event.target))) {
                return callback.call(vm, event);
            }
        };
        document.body.addEventListener('click', el.clickOutsideEvent);
    },
    unbind(el) {
        document.body.removeEventListener('click', el.clickOutsideEvent);
    }
});

ใช้ในส่วนประกอบ:

<li v-click-outside="closeSearch">
  <!-- your component here -->
</li>

สวยเหมือนเดิม @MadisonTrash คำตอบด้านล่าง
retrovertigo

3

ฉันได้อัปเดตคำตอบของ MadisonTrash เพื่อรองรับ Mobile Safari แล้ว (ซึ่งไม่มีclickเหตุการณ์touchendต้องใช้แทน) นอกจากนี้ยังรวมการตรวจสอบเพื่อไม่ให้เหตุการณ์เกิดขึ้นโดยการลากบนอุปกรณ์เคลื่อนที่

Vue.directive('click-outside', {
    bind: function (el, binding, vnode) {
        el.eventSetDrag = function () {
            el.setAttribute('data-dragging', 'yes');
        }
        el.eventClearDrag = function () {
            el.removeAttribute('data-dragging');
        }
        el.eventOnClick = function (event) {
            var dragging = el.getAttribute('data-dragging');
            // Check that the click was outside the el and its children, and wasn't a drag
            if (!(el == event.target || el.contains(event.target)) && !dragging) {
                // call method provided in attribute value
                vnode.context[binding.expression](event);
            }
        };
        document.addEventListener('touchstart', el.eventClearDrag);
        document.addEventListener('touchmove', el.eventSetDrag);
        document.addEventListener('click', el.eventOnClick);
        document.addEventListener('touchend', el.eventOnClick);
    }, unbind: function (el) {
        document.removeEventListener('touchstart', el.eventClearDrag);
        document.removeEventListener('touchmove', el.eventSetDrag);
        document.removeEventListener('click', el.eventOnClick);
        document.removeEventListener('touchend', el.eventOnClick);
        el.removeAttribute('data-dragging');
    },
});

3

ฉันใช้รหัสนี้:

ปุ่มแสดงซ่อน

 <a @click.stop="visualSwitch()"> show hide </a>

แสดงซ่อนองค์ประกอบ

<div class="dialog-popup" v-if="visualState" @click.stop=""></div>

ต้นฉบับ

data () { return {
    visualState: false,
}},
methods: {
    visualSwitch() {
        this.visualState = !this.visualState;
        if (this.visualState)
            document.addEventListener('click', this.visualState);
        else
            document.removeEventListener('click', this.visualState);
    },
},

อัปเดต:ลบนาฬิกา; เพิ่มการขยายพันธุ์หยุด


2

ฉันเกลียดฟังก์ชั่นเพิ่มเติมดังนั้น ... นี่คือโซลูชัน vue ที่ยอดเยี่ยมโดยไม่ต้องใช้วิธี vue เพิ่มเติมเฉพาะ var

  1. สร้างองค์ประกอบ html ตั้งค่าการควบคุมและคำสั่ง
    <p @click="popup = !popup" v-out="popup">

    <div v-if="popup">
       My awesome popup
    </div>
  1. สร้างข้อมูลที่หลากหลายเช่น
data:{
   popup: false,
}
  1. เพิ่ม vue directive ของมัน
Vue.directive('out', {

    bind: function (el, binding, vNode) {
        const handler = (e) => {
            if (!el.contains(e.target) && el !== e.target) {
                //and here is you toggle var. thats it
                vNode.context[binding.expression] = false
            }
        }
        el.out = handler
        document.addEventListener('click', handler)
    },

    unbind: function (el, binding) {
        document.removeEventListener('click', el.out)
        el.out = null
    }
})

2

หากคุณกำลังมองหาการคลิกภายนอกองค์ประกอบโดยเฉพาะ แต่ยังอยู่ในระดับบนสุดคุณสามารถใช้

<div class="parent" @click.self="onParentClick">
  <div class="child"></div>
</div>

ฉันใช้สิ่งนี้สำหรับโมดอล


1

คุณสามารถลงทะเบียนผู้ฟังเหตุการณ์สองคนสำหรับเหตุการณ์คลิกเช่นนี้

document.getElementById("some-area")
        .addEventListener("click", function(e){
        alert("You clicked on the area!");
        e.stopPropagation();// this will stop propagation of this event to upper level
     }
);

document.body.addEventListener("click", 
   function(e) {
           alert("You clicked outside the area!");
         }
);

ขอบคุณ ฉันรู้สิ่งนี้ แต่รู้สึกว่ามันต้องมีวิธีที่ดีกว่านี้ในการทำ Vue.js?

ตกลง! ให้ vue.js อัจฉริยะตอบ :)
saravanakumar

1
  <button 
    class="dropdown"
    @click.prevent="toggle"
    ref="toggle"
    :class="{'is-active': isActiveEl}"
  >
    Click me
  </button>

  data() {
   return {
     isActiveEl: false
   }
  }, 
  created() {
    window.addEventListener('click', this.close);
  },
  beforeDestroy() {
    window.removeEventListener('click', this.close);
  },
  methods: {
    toggle: function() {
      this.isActiveEl = !this.isActiveEl;
    },
    close(e) {
      if (!this.$refs.toggle.contains(e.target)) {
        this.isActiveEl = false;
      }
    },
  },

ขอบคุณทำงานได้อย่างสมบูรณ์และหากคุณต้องการเพียงครั้งเดียวก็ไม่จำเป็นต้องมีห้องสมุดเพิ่มเติม
Marian Klühspies

1

คำตอบสั้น ๆ : สิ่งนี้ควรทำด้วยคำสั่งที่กำหนดเองคำสั่งที่กำหนดเอง

มีคำตอบที่ยอดเยี่ยมมากมายที่นี่เช่นกัน แต่คำตอบส่วนใหญ่ที่ฉันเห็นจะพังทลายลงเมื่อคุณเริ่มใช้การคลิกภายนอกอย่างกว้างขวาง (โดยเฉพาะอย่างยิ่งหลายชั้นหรือมีการยกเว้นหลายรายการ) ฉันได้เขียนบทความเกี่ยวกับสื่อที่พูดถึงความแตกต่างของคำสั่งที่กำหนดเองและการนำไปใช้โดยเฉพาะ อาจไม่ครอบคลุมขอบเคสทั้งหมด แต่ครอบคลุมทุกอย่างที่ฉันคิดไว้

สิ่งนี้จะอธิบายการเชื่อมโยงหลายระดับการยกเว้นองค์ประกอบอื่น ๆ หลายระดับและอนุญาตให้ตัวจัดการของคุณจัดการเฉพาะ "ตรรกะทางธุรกิจ"

นี่คือรหัสสำหรับส่วนความหมายอย่างน้อยที่สุดโปรดดูบทความเพื่อดูคำอธิบายทั้งหมด

var handleOutsideClick={}
const OutsideClick = {
  // this directive is run on the bind and unbind hooks
  bind (el, binding, vnode) {
    // Define the function to be called on click, filter the excludes and call the handler
    handleOutsideClick[el.id] = e => {
      e.stopPropagation()
      // extract the handler and exclude from the binding value
      const { handler, exclude } = binding.value
      // set variable to keep track of if the clicked element is in the exclude list
      let clickedOnExcludedEl = false
      // if the target element has no classes, it won't be in the exclude list skip the check
      if (e.target._prevClass !== undefined) {
        // for each exclude name check if it matches any of the target element's classes
        for (const className of exclude) {
          clickedOnExcludedEl = e.target._prevClass.includes(className)
          if (clickedOnExcludedEl) {
            break // once we have found one match, stop looking
          }
        }
      }
      // don't call the handler if our directive element contains the target element
      // or if the element was in the exclude list
      if (!(el.contains(e.target) || clickedOnExcludedEl)) {
        handler()
      }
    }
    // Register our outsideClick handler on the click/touchstart listeners
    document.addEventListener('click', handleOutsideClick[el.id])
    document.addEventListener('touchstart', handleOutsideClick[el.id])
    document.onkeydown = e => {
      //this is an option but may not work right with multiple handlers
      if (e.keyCode === 27) {
        // TODO: there are minor issues when escape is clicked right after open keeping the old target
        handleOutsideClick[el.id](e)
      }
    }
  },
  unbind () {
    // If the element that has v-outside-click is removed, unbind it from listeners
    document.removeEventListener('click', handleOutsideClick[el.id])
    document.removeEventListener('touchstart', handleOutsideClick[el.id])
    document.onkeydown = null //Note that this may not work with multiple listeners
  }
}
export default OutsideClick

1

ฉันทำวิธีที่แตกต่างออกไปเล็กน้อยโดยใช้ฟังก์ชันภายใน created ()

  created() {
      window.addEventListener('click', (e) => {
        if (!this.$el.contains(e.target)){
          this.showMobileNav = false
        }
      })
  },

ด้วยวิธีนี้หากมีคนคลิกนอกองค์ประกอบในกรณีของฉันระบบนำทางบนอุปกรณ์เคลื่อนที่จะถูกซ่อนไว้

หวังว่านี่จะช่วยได้!


1

มีคำตอบมากมายสำหรับคำถามนี้และส่วนใหญ่จะขึ้นอยู่กับแนวคิดคำสั่งที่กำหนดเองที่คล้ายคลึงกัน ปัญหาของแนวทางนี้คือเราต้องส่งฟังก์ชัน method ไปยังคำสั่งและไม่สามารถเขียนโค้ดได้โดยตรงเหมือนในเหตุการณ์อื่น ๆ

ฉันสร้างแพ็คเกจใหม่vue-on-clickoutที่แตกต่างออกไป ตรวจสอบได้ที่:

อนุญาตให้เขียนv-on:clickoutเหมือนกับเหตุการณ์อื่น ๆ ตัวอย่างเช่นคุณสามารถเขียน

<div v-on:clickout="myField=value" v-on:click="myField=otherValue">...</div>

และมันได้ผล

ปรับปรุง

vue-on-clickout ตอนนี้รองรับ Vue 3!


0

หากใครกำลังมองหาวิธีซ่อนโมดอลเมื่อคลิกนอกโมดอล เนื่องจากโมดอลมักจะมีกระดาษห่อหุ้มที่มีคลาสmodal-wrapหรืออะไรก็ตามที่คุณตั้งชื่อไว้คุณจึงสามารถใส่@click="closeModal"กระดาษห่อหุ้มได้ การใช้การจัดการเหตุการณ์ที่ระบุไว้ในเอกสาร vuejs คุณสามารถตรวจสอบได้ว่าเป้าหมายที่ถูกคลิกนั้นอยู่บน Wrapper หรือในโมดอล

methods: {
  closeModal(e) {
    this.event = function(event) {
      if (event.target.className == 'modal-wrap') {
        // close modal here
        this.$store.commit("catalog/hideModal");
        document.body.removeEventListener("click", this.event);
      }
    }.bind(this);
    document.body.addEventListener("click", this.event);
  },
}
<div class="modal-wrap" @click="closeModal">
  <div class="modal">
    ...
  </div>
<div>


0

โซลูชันของ @Denis Danilenko ใช้ได้กับฉันนี่คือสิ่งที่ฉันทำ: โดยวิธีที่ฉันใช้ VueJS CLI3 และ NuxtJS ที่นี่และกับ Bootstrap4 แต่มันจะทำงานบน VueJS ที่ไม่มี NuxtJS ด้วย:

<div
    class="dropdown ml-auto"
    :class="showDropdown ? null : 'show'">
    <a 
        href="#" 
        class="nav-link" 
        role="button" 
        id="dropdownMenuLink" 
        data-toggle="dropdown" 
        aria-haspopup="true" 
        aria-expanded="false"
        @click="showDropdown = !showDropdown"
        @blur="unfocused">
        <i class="fas fa-bars"></i>
    </a>
    <div 
        class="dropdown-menu dropdown-menu-right" 
        aria-labelledby="dropdownMenuLink"
        :class="showDropdown ? null : 'show'">
        <nuxt-link class="dropdown-item" to="/contact">Contact</nuxt-link>
        <nuxt-link class="dropdown-item" to="/faq">FAQ</nuxt-link>
    </div>
</div>
export default {
    data() {
        return {
            showDropdown: true
        }
    },
    methods: {
    unfocused() {
        this.showDropdown = !this.showDropdown;
    }
  }
}

0

คุณสามารถปล่อยเหตุการณ์จาวาสคริปต์เนทีฟที่กำหนดเองจากคำสั่ง สร้างคำสั่งที่ส่งเหตุการณ์จากโหนดโดยใช้ node.dispatchEvent

let handleOutsideClick;
Vue.directive('out-click', {
    bind (el, binding, vnode) {

        handleOutsideClick = (e) => {
            e.stopPropagation()
            const handler = binding.value

            if (el.contains(e.target)) {
                el.dispatchEvent(new Event('out-click')) <-- HERE
            }
        }

        document.addEventListener('click', handleOutsideClick)
        document.addEventListener('touchstart', handleOutsideClick)
    },
    unbind () {
        document.removeEventListener('click', handleOutsideClick)
        document.removeEventListener('touchstart', handleOutsideClick)
    }
})

ซึ่งสามารถใช้งานได้เช่นนี้

h3( v-out-click @click="$emit('show')" @out-click="$emit('hide')" )

0

ฉันสร้าง div ที่ส่วนท้ายของร่างกายดังนี้:

<div v-if="isPopup" class="outside" v-on:click="away()"></div>

ด้านนอกอยู่ที่ไหน:

.outside {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0px;
  left: 0px;
}

และ away () เป็นวิธีการในอินสแตนซ์ Vue:

away() {
 this.isPopup = false;
}

ง่ายใช้งานได้ดี


0

หากคุณมีองค์ประกอบที่มีหลายองค์ประกอบภายในองค์ประกอบรากคุณสามารถใช้โซลูชันIt just works ™กับบูลีน

<template>
  <div @click="clickInside"></div>
<template>
<script>
export default {
  name: "MyComponent",
  methods: {
    clickInside() {
      this.inside = true;
      setTimeout(() => (this.inside = false), 0);
    },
    clickOutside() {
      if (this.inside) return;
      // handle outside state from here
    }
  },
  created() {
    this.__handlerRef__ = this.clickOutside.bind(this);
    document.body.addEventListener("click", this.__handlerRef__);
  },
  destroyed() {
    document.body.removeEventListener("click", this.__handlerRef__);
  },
};
</script>

0

ใช้แพ็คเกจนี้vue-click-outside

ใช้งานง่ายและเชื่อถือได้โดยแพ็คเกจอื่น ๆ คุณยังสามารถลดขนาดบันเดิลจาวาสคริปต์ของคุณได้โดยเรียกใช้แพ็กเกจในส่วนประกอบที่จำเป็นเท่านั้น (ดูตัวอย่างด้านล่าง)

npm install vue-click-outside

การใช้งาน:

<template>
  <div>
    <div v-click-outside="hide" @click="toggle">Toggle</div>
    <div v-show="opened">Popup item</div>
  </div>
</template>

<script>
import ClickOutside from 'vue-click-outside'

export default {
  data () {
    return {
      opened: false
    }
  },

  methods: {
    toggle () {
      this.opened = true
    },

    hide () {
      this.opened = false
    }
  },

  mounted () {
    // prevent click outside event with popupItem.
    this.popupItem = this.$el
  },

  // do not forget this section
  directives: {
    ClickOutside
  }
}
</script>


0

คุณสามารถสร้างส่วนประกอบใหม่ที่จัดการคลิกภายนอก

Vue.component('click-outside', {
  created: function () {
    document.body.addEventListener('click', (e) => {
       if (!this.$el.contains(e.target)) {
            this.$emit('clickOutside');
           
        })
  },
  template: `
    <template>
        <div>
            <slot/>
        </div>
    </template>
`
})

และใช้ส่วนประกอบนี้:

<template>
    <click-outside @clickOutside="console.log('Click outside Worked!')">
      <div> Your code...</div>
    </click-outside>
</template>

-1

บ่อยครั้งที่ผู้คนต้องการทราบว่าผู้ใช้ออกจากองค์ประกอบราก (ทำงานร่วมกับส่วนประกอบระดับใดก็ได้)

Vue({
  data: {},
  methods: {
    unfocused : function() {
      alert('good bye');
    }
  }
})
<template>
  <div tabindex="1" @blur="unfocused">Content inside</div>
</template>


-1

ฉันมีวิธีจัดการกับเมนูแบบเลื่อนลงสลับ:

export default {
data() {
  return {
    dropdownOpen: false,
  }
},
methods: {
      showDropdown() {
        console.log('clicked...')
        this.dropdownOpen = !this.dropdownOpen
        // this will control show or hide the menu
        $(document).one('click.status', (e)=> {
          this.dropdownOpen = false
        })
      },
}

-1

ฉันใช้แพ็คเกจนี้: https://www.npmjs.com/package/vue-click-outside

มันใช้ได้ดีสำหรับฉัน

HTML:

<div class="__card-content" v-click-outside="hide" v-if="cardContentVisible">
    <div class="card-header">
        <input class="subject-input" placeholder="Subject" name=""/>
    </div>
    <div class="card-body">
        <textarea class="conversation-textarea" placeholder="Start a conversation"></textarea>
    </div>
</div>

รหัสสคริปต์ของฉัน:

import ClickOutside from 'vue-click-outside'
export default
{
    data(){
        return {
            cardContentVisible:false
        }
    },
    created()
    {
    },
    methods:
        {
            openCardContent()
            {
                this.cardContentVisible = true;
            }, hide () {
            this.cardContentVisible = false
                }
        },
    directives: {
            ClickOutside
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.