ฉันจะตรวจสอบการคลิกนอกองค์ประกอบได้อย่างไร


2486

ฉันมีเมนู HTML บางอย่างซึ่งฉันแสดงอย่างสมบูรณ์เมื่อผู้ใช้คลิกที่ส่วนหัวของเมนูเหล่านี้ ฉันต้องการซ่อนองค์ประกอบเหล่านี้เมื่อผู้ใช้คลิกนอกพื้นที่ของเมนู

สิ่งนี้เป็นไปได้ด้วย jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});

47
นี่คือตัวอย่างของกลยุทธ์นี้: jsfiddle.net/tedp/aL7Xe/1
เท็ด

18
ดังที่ทอมเอ่ยถึงคุณจะต้องอ่านcss-tricks.com/dangers-stopping-event-propagationก่อนที่จะใช้วิธีนี้ เครื่องมือ jsfiddle นั้นค่อนข้างเจ๋ง
Jon Coombs

3
ได้รับการอ้างอิงไปยังองค์ประกอบแล้ว event.target และในที่สุด = == หรือทั้งสองของพวกเขาแล้วรันโค้ดตาม .. !
Rohit มาร์

ฉันแนะนำให้ใช้github.com/gsantiago/jquery-clickout :)
Rômulo M. Farias

2
วิธีการแก้ปัญหา Vanilla JSด้วยevent.targetและโดยไม่ต้อง event.stopPropagation
lowtechsun

คำตอบ:


1812

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

แนบเหตุการณ์การคลิกเข้ากับตัวเอกสารซึ่งปิดหน้าต่าง แนบเหตุการณ์คลิกแยกไปยังคอนเทนเนอร์ซึ่งหยุดการเผยแพร่ไปยังเนื้อหาเอกสาร

$(window).click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

708
สิ่งนี้จะหยุดพฤติกรรมมาตรฐานของหลาย ๆ อย่างรวมถึงปุ่มและลิงก์ภายใน #menucontainer ฉันประหลาดใจที่คำตอบนี้เป็นที่นิยมมาก
ศิลปะ

75
สิ่งนี้ไม่ทำลายพฤติกรรมของสิ่งใดใน #menucontainer เนื่องจากอยู่ด้านล่างสุดของห่วงโซ่การแพร่กระจายสำหรับสิ่งที่อยู่ภายใน
Eran Galperin

94
มันสวยงามมาก แต่คุณไม่ควรใช้$('html').click()ร่างกาย ร่างกายมีความสูงของเนื้อหาอยู่เสมอ มันมีเนื้อหาไม่มากหรือหน้าจอสูงมากมันใช้งานได้เฉพาะส่วนที่เต็มไปด้วยร่างกาย
แม้ว

103
ฉันยังประหลาดใจที่โซลูชันนี้ได้รับคะแนนเสียงมากมาย สิ่งนี้จะล้มเหลวสำหรับองค์ประกอบภายนอกใด ๆ ที่มี stopPropagation jsfiddle.net/Flandre/vaNFw/3
Andre

140
ฟิลิปวอลตันอธิบายได้เป็นอย่างดีว่าทำไมคำตอบนี้ไม่ได้เป็นทางออกที่ดีที่สุด: css-tricks.com/dangers-stopping-event-propagation
ทอม

1386

คุณสามารถฟังเหตุการณ์คลิกได้documentแล้วให้แน่ใจว่าไม่ได้เป็นปู่ย่าตายายหรือเป้าหมายขององค์ประกอบคลิกโดยใช้#menucontainer .closest()

หากไม่เป็นเช่นนั้นองค์ประกอบที่ถูกคลิกนั้นอยู่นอก #menucontainerและคุณสามารถซ่อนได้อย่างปลอดภัย

$(document).click(function(event) { 
  $target = $(event.target);
  if(!$target.closest('#menucontainer').length && 
  $('#menucontainer').is(":visible")) {
    $('#menucontainer').hide();
  }        
});

แก้ไข - 2017-06-23

คุณสามารถล้างข้อมูลหลังจากผู้ฟังเหตุการณ์ได้หากคุณวางแผนที่จะยกเลิกเมนูและต้องการหยุดฟังเหตุการณ์ ฟังก์ชั่นนี้จะทำความสะอาดเฉพาะผู้ฟังที่สร้างขึ้นใหม่เท่านั้นdocumentเท่านั้น ด้วยไวยากรณ์ ES2015:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    $target = $(event.target);
    if (!$target.closest(selector).length && $(selector).is(':visible')) {
        $(selector).hide();
        removeClickListener();
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

แก้ไข - 2018-03-11

สำหรับผู้ที่ไม่ต้องการใช้ jQuery นี่คือโค้ดด้านบนใน vanillaJS ธรรมดา (ECMAScript6)

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

บันทึก: นี่เป็นความคิดเห็นของ Alex ที่จะใช้!element.contains(event.target)แทน jQuery part

แต่element.closest()ตอนนี้มีให้ใช้งานในเบราว์เซอร์หลักทุกรุ่น (รุ่น W3C แตกต่างจาก jQuery หนึ่งเล็กน้อย) Polyfills สามารถพบได้ที่นี่: Element.closest ()

แก้ไข - 2020-05-21

ในกรณีที่คุณต้องการให้ผู้ใช้สามารถคลิกและลากภายในองค์ประกอบจากนั้นปล่อยเมาส์นอกองค์ประกอบโดยไม่ต้องปิดองค์ประกอบ:

      ...
      let lastMouseDownX = 0;
      let lastMouseDownY = 0;
      let lastMouseDownWasOutside = false;

      const mouseDownListener = (event: MouseEvent) => {
        lastMouseDownX = event.offsetX
        lastMouseDownY = event.offsetY
        lastMouseDownWasOutside = !$(event.target).closest(element).length
      }
      document.addEventListener('mousedown', mouseDownListener);

และในoutsideClickListener:

const outsideClickListener = event => {
        const deltaX = event.offsetX - lastMouseDownX
        const deltaY = event.offsetY - lastMouseDownY
        const distSq = (deltaX * deltaX) + (deltaY * deltaY)
        const isDrag = distSq > 3
        const isDragException = isDrag && !lastMouseDownWasOutside

        if (!element.contains(event.target) && isVisible(element) && !isDragException) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
          document.removeEventListener('mousedown', mouseDownListener); // Or add this line to removeClickListener()
        }
    }

28
ฉันลองคำตอบอื่น ๆ อีกมากมาย แต่มีเพียงอันเดียวเท่านั้นที่ใช้ได้ ขอบคุณ รหัสที่ฉันใช้คือ: $ (เอกสาร) .click (ฟังก์ชัน (เหตุการณ์) {ถ้า ($ (event.target) .closest ('. หน้าต่าง'). ความยาว == 0) {$ ('. หน้าต่าง' ) .fadeOut ('fast');}});
Pistos

39
ฉันลงเอยด้วยวิธีนี้จริง ๆ เพราะมันรองรับหลายเมนูในหน้าเดียวกันซึ่งการคลิกที่เมนูที่สองในขณะที่การเปิดครั้งแรกจะเป็นการเปิดครั้งแรกในโซลูชัน stopPropagation
umassthrower

13
คำตอบที่ยอดเยี่ยม นี่คือวิธีที่จะไปเมื่อคุณมีหลายรายการที่คุณต้องการปิด
จอห์น

5
นี่ควรเป็นคำตอบที่ได้รับการยอมรับเนื่องจากมีข้อบกพร่องอื่น ๆ ที่มีวิธีแก้ไขเหตุการณ์
มะเขือเทศ

20
Without jQuery - !element.contains(event.target)using Node.contain ()
Alex Ross

303

วิธีการตรวจสอบการคลิกนอกองค์ประกอบ?

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

ฉันต้องการซ่อนองค์ประกอบเหล่านี้เมื่อผู้ใช้คลิกนอกพื้นที่ของเมนู

นี่คือสาเหตุอันสูงส่งและเป็นปัญหาที่แท้จริง ชื่อคำถาม - ซึ่งเป็นคำตอบที่ดูเหมือนจะพยายามตอบ - ประกอบด้วยปลาเฮอริ่งแดงที่โชคร้าย

คำแนะนำ: เป็นคำว่า"คลิก" !

คุณไม่ต้องการผูกตัวจัดการการคลิก

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

งั้นลองใช้คำถามใหม่อีกครั้ง

หนึ่งจะปิดกล่องโต้ตอบเมื่อผู้ใช้เสร็จสิ้นด้วยมันได้อย่างไร

นี่คือเป้าหมาย น่าเสียดายที่ตอนนี้เราต้องผูกuserisfinishedwiththedialogเหตุการณ์และการผูกนั้นไม่ตรงไปตรงมา

ดังนั้นเราจะตรวจสอบได้อย่างไรว่าผู้ใช้เสร็จสิ้นการใช้กล่องโต้ตอบ?

focusout เหตุการณ์

การเริ่มต้นที่ดีคือการพิจารณาว่าโฟกัสได้ออกจากกล่องโต้ตอบหรือไม่

คำแนะนำ: ระวังด้วยblurเหตุการณ์blurอย่าเผยแพร่ถ้าเหตุการณ์นั้นผูกพันกับเฟสเดือดดาล!

jQuery's focusoutจะทำได้ดี หากคุณไม่สามารถใช้ jQuery คุณสามารถใช้blurระหว่างขั้นตอนการดักจับ:

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

นอกจากนี้สำหรับการสนทนาหลาย ๆ ครั้งคุณจะต้องอนุญาตให้คอนเทนเนอร์ได้รับโฟกัส เพิ่มtabindex="-1"เพื่อให้ไดอะล็อกได้รับการโฟกัสแบบไดนามิกโดยไม่รบกวนการไหลของการแท็บ

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


หากคุณเล่นกับตัวอย่างนั้นเป็นเวลานานกว่าหนึ่งนาทีคุณควรเริ่มเห็นปัญหาอย่างรวดเร็ว

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

การแก้ไขคือการจัดคิวการเปลี่ยนแปลงสถานะบนลูปเหตุการณ์ ซึ่งสามารถทำได้โดยการใช้setImmediate(...)หรือเบราว์เซอร์ที่ไม่สนับสนุนsetTimeout(..., 0) setImmediateเมื่อเข้าคิวแล้วสามารถยกเลิกได้ในภายหลังfocusin:

$('.submenu').on({
  focusout: function (e) {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function (e) {
    clearTimeout($(this).data('submenuTimer'));
  }
});

ปัญหาที่สองคือกล่องโต้ตอบจะไม่ปิดลงเมื่อมีการกดลิงค์อีกครั้ง นี่เป็นเพราะไดอะล็อกสูญเสียการโฟกัสทริกเกอร์ลักษณะการปิดหลังจากที่การคลิกลิงค์ทริกเกอร์ไดอะล็อกเพื่อเปิดอีกครั้ง

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

สิ่งนี้ควรดูคุ้นเคย
$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});


Esc สำคัญ

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

นี่มักจะเป็นคุณสมบัติ "ดีที่มี" แต่เป็นเรื่องปกติที่เมื่อคุณมีคำกริยาหรือป๊อปอัพของการเรียงลำดับใด ๆ ที่Escสำคัญจะปิดมัน

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}


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

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}


บทบาทของ WAI-ARIA และการสนับสนุนการเข้าถึงอื่น ๆ

คำตอบนี้หวังว่าจะครอบคลุมพื้นฐานของการรองรับแป้นพิมพ์และเมาส์ที่ใช้งานได้สำหรับคุณลักษณะนี้ แต่เนื่องจากมันมีขนาดค่อนข้างใหญ่อยู่แล้วฉันจะหลีกเลี่ยงการอภิปรายเกี่ยวกับบทบาทและคุณลักษณะของWAI-ARIAแต่ฉันขอแนะนำให้ผู้ใช้ เกี่ยวกับบทบาทที่ควรใช้และคุณลักษณะอื่น ๆ ที่เหมาะสม


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

4
น่าอัศจรรย์อธิบายได้ดี ฉันเพิ่งใช้วิธีนี้กับ React Component และทำงานได้อย่างสมบูรณ์แบบขอบคุณ
David Lavieri

2
@zzzzBov ขอบคุณสำหรับคำตอบในเชิงลึกฉันพยายามที่จะบรรลุมันในวานิลลา JS และฉันก็หายไปเล็กน้อยกับทุกสิ่งที่ jquery มีอะไรที่คล้ายกันในวานิลลา js?
HendrikEng

2
@zzzzBov ไม่ฉันไม่ได้มองหาคุณที่จะเขียนรุ่น jQuery ฟรีแน่นอนฉันจะพยายามทำมันและฉันเดาที่ดีที่สุดคือการถามคำถามใหม่ที่นี่ถ้าฉันติดจริงๆ ขอบคุณมากอีกครั้ง
HendrikEng

7
แม้ว่านี่จะเป็นวิธีการที่ยอดเยี่ยมในการตรวจจับการคลิกออกจากรายการดรอปดาวน์ที่กำหนดเองหรืออินพุตอื่น ๆ แต่ก็ไม่ควรเป็นวิธีที่ต้องการสำหรับโมดอลหรือป๊อปอัปด้วยเหตุผลสองประการ โมดอลจะปิดเมื่อผู้ใช้สลับไปยังแท็บหรือหน้าต่างอื่นหรือเปิดเมนูบริบทซึ่งน่ารำคาญจริงๆ นอกจากนี้เหตุการณ์ 'คลิก' จะยิงเมาส์ขึ้นในขณะที่เหตุการณ์ 'โฟกัส' จะกระตุ้นให้คุณกดเมาส์ลงทันที โดยปกติแล้วปุ่มจะทำงานเฉพาะเมื่อคุณกดปุ่มเมาส์ลงแล้วปล่อย วิธีที่เหมาะสมและเข้าถึงได้สำหรับการทำ Modals คือการเพิ่มปุ่มปิด tabbable
เควิน

144

โซลูชันอื่นที่นี่ไม่ได้ผลสำหรับฉันดังนั้นฉันจึงต้องใช้:

if(!$(event.target).is('#foo'))
{
    // hide menu
}

ฉันได้โพสต์อีกตัวอย่างการใช้งานจริงของ event.target เพื่อหลีกเลี่ยงการเรียกใช้วิดเจ็ต Jquery UI อื่น ๆ นอกคลิกตัวจัดการ html เมื่อฝังไว้ในกล่องป๊อปอัปของคุณเอง: วิธีที่ดีที่สุดเพื่อให้ได้เป้าหมายดั้งเดิม
Joey T

43
สิ่งนี้ใช้ได้สำหรับฉันยกเว้นฉันเพิ่ม&& !$(event.target).parents("#foo").is("#foo")ไว้ในIFคำสั่งเพื่อให้องค์ประกอบลูก ๆ จะไม่ปิดเมนูเมื่อคลิก
honyovk

1
ไม่สามารถหาได้ดีกว่า: // เราอยู่ด้านนอก $ (event.target) .parents ('# foo'). length == 0
AlexG

3
การปรับปรุงที่รัดกุมในการจัดการกับการทำรังลึกคือการใช้.is('#foo, #foo *')แต่ผมไม่แนะนำให้มีผลผูกพันไสคลิกเพื่อแก้ปัญหานี้
zzzzBov

1
!$(event.target).closest("#foo").lengthจะดีกว่าและขัดขวางความจำเป็นในการเพิ่ม @ honyovk
tvanc

127

ฉันมีแอปพลิเคชั่นที่ทำงานคล้ายกับตัวอย่างของ Eran ยกเว้นฉันแนบเหตุการณ์การคลิกไปที่เนื้อหาเมื่อฉันเปิดเมนู ...

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

ข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชั่นของ jQueryone()


9
แต่แล้วถ้าคุณคลิกที่เมนูตัวเองแล้วออกไปข้างนอกมันจะไม่ทำงาน :)
vSync

3
มันจะช่วยให้ใส่ event.stopProgagantion () ก่อนผูกตัวฟังการคลิกกับเนื้อหา
Jasper Kennis

4
ปัญหานี้คือ "หนึ่ง" นำไปใช้กับวิธีการ jQuery ของการเพิ่มกิจกรรมในอาร์เรย์หลายครั้ง ดังนั้นหากคุณคลิกที่เมนูเพื่อเปิดมากกว่าหนึ่งครั้งเหตุการณ์จะถูกผูกไว้กับร่างกายอีกครั้งและพยายามซ่อนเมนูหลายครั้ง ควรใช้ Failafe เพื่อแก้ไขปัญหานี้
markyzm

หลังจากที่มีผลผูกพันใช้.one- ภายในของ$('html')การจัดการ - $('html').off('click')การเขียน
โคดี

4
@ เนื่องจากฉันไม่คิดว่าจะช่วยได้ oneจัดการจะเรียกโดยอัตโนมัติoff(ตามที่แสดงในเอกสาร jQuery) ที่
Mariano Desanze

44

หลังจากการวิจัยฉันพบวิธีแก้ไขปัญหาสามข้อ (ฉันลืมลิงค์ของเพจเพื่ออ้างอิง)

ทางออกแรก

<script>
    //The good thing about this solution is it doesn't stop event propagation.

    var clickFlag = 0;
    $('body').on('click', function () {
        if(clickFlag == 0) {
            console.log('hide element here');
            /* Hide element here */
        }
        else {
            clickFlag=0;
        }
    });
    $('body').on('click','#testDiv', function (event) {
        clickFlag = 1;
        console.log('showed the element');
        /* Show the element */
    });
</script>

ทางออกที่สอง

<script>
    $('body').on('click', function(e) {
        if($(e.target).closest('#testDiv').length == 0) {
           /* Hide dropdown here */
        }
    });
</script>

ทางออกที่สาม

<script>
    var specifiedElement = document.getElementById('testDiv');
    document.addEventListener('click', function(event) {
        var isClickInside = specifiedElement.contains(event.target);
        if (isClickInside) {
          console.log('You clicked inside')
        }
        else {
          console.log('You clicked outside')
        }
    });
</script>

8
วิธีที่สามคือวิธีการตรวจสอบที่หรูหราที่สุด มันไม่เกี่ยวข้องกับค่าใช้จ่ายใด ๆ ของ jQuery ดีมาก. มันช่วยได้มาก ขอบคุณ
dbarth

ฉันกำลังพยายามหาทางแก้ปัญหาข้อที่สามที่มีองค์ประกอบหลายอย่างdocument.getElementsByClassNameถ้าใครบางคนมีเงื่อนงำโปรดแบ่งปัน
lowtechsun

@lowtechsun คุณต้องวนซ้ำเพื่อตรวจสอบแต่ละรายการ
Donnie D'Amato

ชอบโซลูชันที่สามมากที่สุด แต่การคลิกทริกเกอร์ก่อนที่ div ของฉันจะเริ่มแสดงให้เห็นว่ามันซ่อนอะไรอีก
indiehjaerta

อันที่สามอนุญาตให้ใช้สำหรับ console.log ได้ แต่ไม่อนุญาตให้คุณปิดจริง ๆ โดยตั้งค่าการแสดงผลเป็น none - เพราะจะทำเช่นนี้ก่อนที่เมนูจะปรากฏขึ้น คุณมีทางออกสำหรับสิ่งนี้หรือไม่?
RolandiXor

40
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

ทำงานให้ฉันได้ดี


3
นี่คือสิ่งที่ฉันต้องการใช้ มันอาจไม่สมบูรณ์แบบ แต่ในฐานะโปรแกรมเมอร์งานอดิเรกมันง่ายพอที่จะเข้าใจอย่างชัดเจน
kevtrout

เบลอคือการเคลื่อนไหวนอก#menucontainerคำถามคือเกี่ยวกับคลิก
borrel

4
@ บอเรลเบลอไม่ได้ย้ายออกไปนอกภาชนะ เบลออยู่ตรงข้ามกับการโฟกัสคุณกำลังคิดถึงเม้าส์ โซลูชันนี้ทำงานได้ดีสำหรับฉันโดยเฉพาะเมื่อฉันสร้างข้อความ "คลิกเพื่อแก้ไข" ซึ่งฉันสลับไปมาระหว่างช่องข้อความธรรมดากับช่องป้อนข้อมูลเมื่อมีการคลิก
parker.sikand

ฉันต้องเพิ่มtabindex="-1"ไปยัง#menuscontainerเพื่อให้ทำงานได้ ดูเหมือนว่าถ้าคุณใส่แท็กอินพุตภายในคอนเทนเนอร์และคลิกที่มันคอนเทนเนอร์จะถูกซ่อน
tyrion

เหตุการณ์ mouseleave เหมาะสมกว่าสำหรับเมนูและคอนเทนเนอร์ (อ้างอิง: w3schools.com/jquery/…
......

38

ขณะนี้มีปลั๊กอินสำหรับ: นอกเหตุการณ์ ( โพสต์บล็อก )

สิ่งต่อไปนี้เกิดขึ้นเมื่อตัวจัดการการคลิกออกด้านข้าง (WLOG) ถูกผูกไว้กับองค์ประกอบ:

  • องค์ประกอบจะถูกเพิ่มไปยังอาร์เรย์ซึ่งมีองค์ประกอบทั้งหมดที่มีตัวจัดการclickoutside
  • ตัวจัดการการคลิก ( เนมสเปซ ) ถูกผูกไว้กับเอกสาร (หากยังไม่มี)
  • ในการคลิกใด ๆในเอกสารเหตุการณ์clickoutsideจะถูกเรียกใช้สำหรับองค์ประกอบเหล่านั้นในอาร์เรย์ที่ไม่เท่ากับหรือเป็นพาเรนต์ของเป้าหมายการคลิกเหตุการณ์
  • นอกจากนี้ event.target สำหรับเหตุการณ์clickoutจะถูกตั้งค่าเป็นองค์ประกอบที่ผู้ใช้คลิก (เพื่อให้คุณรู้ว่าผู้ใช้คลิกอะไรไม่ใช่แค่คลิกด้านนอก)

ดังนั้นจึงไม่มีเหตุการณ์ใดที่จะหยุดการเผยแพร่และอาจใช้ตัวจัดการการคลิกเพิ่มเติม"ด้านบน" องค์ประกอบที่มีตัวจัดการภายนอก


ปลั๊กอินที่ดี ลิงก์ใน "กิจกรรมภายนอก" ตายในขณะที่ลิงก์โพสต์บล็อกยังมีชีวิตอยู่และให้ปลั๊กอินที่มีประโยชน์มากสำหรับกิจกรรมประเภท "clickoutside" นอกจากนี้ยังได้รับอนุญาตจาก MIT
TechNyquist

ปลั๊กอินที่ยอดเยี่ยม ทำงานได้อย่างสมบูรณ์แบบ การใช้งานเป็นเช่น:$( '#element' ).on( 'clickoutside', function( e ) { .. } );
Gavin

33

สิ่งนี้ใช้ได้สำหรับฉันอย่างสมบูรณ์แบบ !!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});

โซลูชันนี้ทำงานได้ดีสำหรับสิ่งที่ฉันกำลังทำอยู่ซึ่งฉันยังต้องการกิจกรรมการคลิกเพื่อแสดงความขอบคุณ
Mike

26

ฉันไม่คิดว่าสิ่งที่คุณต้องการจริงๆคือปิดเมนูเมื่อผู้ใช้คลิกนอก สิ่งที่คุณต้องการสำหรับเมนูที่จะปิดเมื่อผู้ใช้คลิกที่ใดก็ได้บนเพจ หากคุณคลิกที่เมนูหรือปิดเมนูก็ควรปิดใช่ไหม

การค้นหาคำตอบที่ไม่น่าพอใจข้างต้นกระตุ้นให้ฉันเขียนบล็อกนี้โพสต์เมื่อวันก่อน สำหรับคนที่คลั่งไคล้มากขึ้นมีจำนวน gotchas ที่จะต้องทราบคือ:

  1. หากคุณแนบตัวจัดการเหตุการณ์คลิกเข้ากับองค์ประกอบร่างกายในเวลาคลิกอย่าลืมคลิกครั้งที่สองก่อนที่จะปิดเมนูและยกเลิกการเชื่อมโยงเหตุการณ์ มิฉะนั้นเหตุการณ์การคลิกที่เปิดเมนูจะทำให้ผู้ฟังที่ต้องปิดเมนูขึ้นไป
  2. หากคุณใช้ event.stopPropogation () กับการคลิกจะไม่มีองค์ประกอบอื่นใดในหน้าของคุณที่สามารถมีคุณสมบัติคลิกได้ทุกที่เพื่อปิด
  3. การแนบตัวจัดการเหตุการณ์คลิกเข้ากับองค์ประกอบของร่างกายอย่างไม่มีกำหนดไม่ใช่วิธีแก้ไข
  4. การเปรียบเทียบเป้าหมายของเหตุการณ์และผู้ปกครองกับผู้สร้างของตัวจัดการถือว่าสิ่งที่คุณต้องการคือการปิดเมนูเมื่อคุณคลิกมันเมื่อสิ่งที่คุณต้องการคือการปิดเมื่อคุณคลิกที่ใดก็ได้บนหน้า
  5. การฟังเหตุการณ์ในองค์ประกอบของร่างกายจะทำให้โค้ดของคุณเปราะมากขึ้น การจัดแต่งทรงผมที่ไร้เดียงสาอย่างนี้จะทำให้มันพัง:body { margin-left:auto; margin-right: auto; width:960px;}

2
"ถ้าคุณคลิกที่เมนูหรือปิดเมนูมันควรจะปิดทันที" ไม่เสมอ. การยกเลิกการคลิกโดยการลากองค์ประกอบจะยังคงก่อให้เกิดการคลิกระดับเอกสาร แต่เจตนาจะไม่เป็นการปิดเมนูต่อไป นอกจากนี้ยังมีกล่องโต้ตอบประเภทอื่น ๆ อีกมากมายที่สามารถใช้พฤติกรรม "คลิกออก" ที่จะอนุญาตให้คลิกภายใน
zzzzBov

25

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

$('#menuscontainer').click(function(event) {
    //your code that shows the menus fully

    //now set up an event listener so that clicking anywhere outside will close the menu
    $('html').click(function(event) {
        //check up the tree of the click target to check whether user has clicked outside of menu
        if ($(event.target).parents('#menuscontainer').length==0) {
            // your code to hide menu

            //this event listener has done its job so we can unbind it.
            $(this).unbind(event);
        }

    })
});

25

ทางออกที่ง่ายสำหรับสถานการณ์คือ:

$(document).mouseup(function (e)
{
    var container = $("YOUR SELECTOR"); // Give you class or ID

    if (!container.is(e.target) &&            // If the target of the click is not the desired div or section
        container.has(e.target).length === 0) // ... nor a descendant-child of the container
    {
        container.hide();
    }
});

สคริปต์ด้านบนจะซ่อนdivหากdivมีการเรียกใช้กิจกรรมนอกคลิก

คุณสามารถดูบล็อกต่อไปนี้สำหรับข้อมูลเพิ่มเติม: http://www.codecanal.com/detect-click-outside-div-using-javascript/


22

Solution1

แทนที่จะใช้ event.stopPropagation () ซึ่งอาจมีผลข้างเคียงเพียงแค่กำหนดตัวแปรแฟล็กอย่างง่ายและเพิ่มหนึ่งifเงื่อนไข ฉันทดสอบและทำงานอย่างถูกต้องโดยไม่มีผลกระทบใด ๆ ของ stopPropagation:

var flag = "1";
$('#menucontainer').click(function(event){
    flag = "0"; // flag 0 means click happened in the area where we should not do any action
});

$('html').click(function() {
    if(flag != "0"){
        // Hide the menus if visible
    }
    else {
        flag = "1";
    }
});

Solution2

ด้วยifเงื่อนไขง่ายๆ:

$(document).on('click', function(event){
    var container = $("#menucontainer");
    if (!container.is(event.target) &&            // If the target of the click isn't the container...
        container.has(event.target).length === 0) // ... nor a descendant of the container
    {
        // Do whatever you want to do when click is outside the element
    }
});

ผมใช้วิธีนี้กับธงบูลีนและเป็นเรื่องที่ดีนอกจากนี้ยังมีรถบรรทุก DOM และถ้า #menucontainer ภายในมีจำนวนมากขององค์ประกอบอื่น ๆ
Migio B

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

21

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

หรือตรวจสอบตำแหน่งของการคลิกและดูว่ามีอยู่ในพื้นที่เมนูหรือไม่


18

ฉันประสบความสำเร็จกับบางสิ่งเช่นนี้:

var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});

เหตุผลคือเมื่อ#menuscontainerมีการเชื่อมโยงตัวจัดการการคลิกกับเนื้อหาที่ซ่อน#menuscontainerเฉพาะในกรณีที่เป้าหมาย (ของการคลิก) ไม่ใช่ลูกของมัน


17

เป็นตัวแปร:

var $menu = $('#menucontainer');
$(document).on('click', function (e) {

    // If element is opened and click target is outside it, hide it
    if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
        $menu.hide();
    }
});

ไม่มีปัญหาในการหยุดการเผยแพร่เหตุการณ์และรองรับหลายเมนูในหน้าเดียวกันโดยคลิกที่เมนูที่สองในขณะที่เมนูแรกเปิดขึ้นจะเป็นการเปิดครั้งแรกในโซลูชัน stopPropagation


16

กรณีที่มีคุณสมบัติที่เรียกว่า event.path ขององค์ประกอบซึ่งเป็นรายการ "คงที่ได้รับคำสั่งของบรรพบุรุษของมันทั้งหมดเพื่อต้นไม้" ในการตรวจสอบว่ามีเหตุการณ์ที่เกิดขึ้นจากองค์ประกอบ DOM ที่เฉพาะเจาะจงหรือหนึ่งในลูก ๆ ของมันเพียงตรวจสอบเส้นทางสำหรับองค์ประกอบ DOM ที่เฉพาะเจาะจงนั้น นอกจากนี้ยังสามารถใช้ในการตรวจสอบองค์ประกอบหลายรายการโดยการตรวจสอบองค์ประกอบแบบมีเหตุผลORในsomeฟังก์ชัน

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
  background:yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

ดังนั้นสำหรับกรณีของคุณมันควรจะเป็น

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});

1
ไม่ทำงานใน Edge
ตำนาน

event.pathไม่ใช่สิ่ง
Andrew

14

ฉันพบวิธีนี้ในปลั๊กอินปฏิทิน jQuery

function ClickOutsideCheck(e)
{
  var el = e.target;
  var popup = $('.popup:visible')[0];
  if (popup==undefined)
    return true;

  while (true){
    if (el == popup ) {
      return true;
    } else if (el == document) {
      $(".popup").hide();
      return false;
    } else {
      el = $(el).parent()[0];
    }
  }
};

$(document).bind('mousedown.popup', ClickOutsideCheck);

13

นี่คือวิธีการแก้ปัญหาวานิลลา JavaScript สำหรับผู้ชมในอนาคต

เมื่อคลิกองค์ประกอบใด ๆ ภายในเอกสารหากรหัสขององค์ประกอบที่ถูกคลิกถูกสลับหรือองค์ประกอบที่ซ่อนไม่ได้ถูกซ่อนและองค์ประกอบที่ซ่อนไม่ได้มีองค์ประกอบที่ถูกคลิกสลับองค์ประกอบ

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();

หากคุณต้องการสลับหลายอันในหน้าเดียวกันคุณสามารถใช้สิ่งนี้:

  1. เพิ่มชื่อคลาสhiddenลงในรายการที่ยุบได้
  2. เมื่อคลิกที่เอกสารให้ปิดองค์ประกอบที่ซ่อนอยู่ทั้งหมดซึ่งไม่มีองค์ประกอบที่ถูกคลิกและจะไม่ถูกซ่อน
  3. หากองค์ประกอบที่ถูกคลิกเป็นสลับให้สลับองค์ประกอบที่ระบุ

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>


13

ฉันประหลาดใจที่ไม่มีใครตอบรับfocusoutเหตุการณ์จริง:

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>


คุณคิดถึงคำตอบของฉันฉันคิดว่า stackoverflow.com/a/47755925/6478359
Muhammet Can TONBUL

นี่ควรเป็นคำตอบที่ได้รับการยอมรับตรงประเด็น ขอบคุณ !!!
ลัคกี้ลัม

8

หากคุณกำลังเขียนสคริปต์สำหรับ IE และ FF 3 * และคุณเพียงต้องการที่จะรู้ว่าการคลิกเกิดขึ้นภายในพื้นที่กล่องบางอย่างคุณสามารถใช้สิ่งที่ชอบ:

this.outsideElementClick = function(objEvent, objElement){   
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;

if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
    blnInsideX = true;

if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
    blnInsideY = true;

if (blnInsideX && blnInsideY)
    return false;
else
    return true;}

8

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

$(document).on("click.menu-outside", function(event){
    // Test if target and it's parent aren't #menuscontainer
    // That means the click event occur on other branch of document tree
    if(!$(event.target).parents().andSelf().is("#menuscontainer")){
        // Click outisde #menuscontainer
        // Hide the menus (but test if menus aren't already hidden)
    }
});

ในการลบคลิกออกไปด้านนอกฟังเหตุการณ์เพียง:

$(document).off("click.menu-outside");

สำหรับฉันนี่เป็นทางออกที่ดีที่สุด ใช้มันในการโทรกลับหลังจากภาพเคลื่อนไหวดังนั้นฉันจึงจำเป็นต้องแยกกิจกรรมบางอย่างออกมา +1
qwertzman

มีการตรวจสอบซ้ำซ้อนที่นี่ ถ้าองค์ประกอบเป็นจริง#menuscontainerคุณยังคงผ่านมันเป็นผู้ปกครอง คุณควรตรวจสอบสิ่งนั้นก่อนและหากไม่ใช่องค์ประกอบนั้นให้ไปที่แผนผัง DOM
vsync

ถูกต้อง! if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){คุณสามารถเปลี่ยนสภาพไป มันเป็นการปรับให้เหมาะสมเล็กน้อย แต่เกิดขึ้นเพียงไม่กี่ครั้งในช่วงอายุโปรแกรมของคุณ: สำหรับการคลิกแต่ละครั้งหากclick.menu-outsideมีการลงทะเบียนเหตุการณ์ ยาวกว่า (+32 ตัวอักษร) และไม่ใช้วิธีการผูกมัด
mems

8

ใช้:

var go = false;
$(document).click(function(){
    if(go){
        $('#divID').hide();
        go = false;
    }
})

$("#divID").mouseover(function(){
    go = false;
});

$("#divID").mouseout(function (){
    go = true;
});

$("btnID").click( function(){
    if($("#divID:visible").length==1)
        $("#divID").hide(); // Toggle
    $("#divID").show();
});

7

หากมีคนอยากรู้อยากเห็นที่นี่เป็นวิธีแก้ปัญหาจาวาสคริปต์ (es6):

window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })

และ es5 ในกรณี:

window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}

});


7

นี่คือทางออกที่ง่ายโดยจาวาสคริปต์ที่บริสุทธิ์ เป็นรุ่นล่าสุดด้วย ES6 :

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})

"Up-to-date กับ ES6" เป็นข้อเรียกร้องที่เป็นตัวหนาสวยเมื่อสิ่งเดียวที่ up-to-date กับ ES6 จะทำแทน() => {} function() {}สิ่งที่คุณมีอยู่นั้นถูกจัดว่าเป็น JavaScript ธรรมดาที่มีการบิด ES6
MortenMoulder

@MortenMoulder: ใช่ มันแค่ให้ความสนใจแม้ว่ามันจะเป็น ES6 จริงๆ แต่ลองดูวิธีการแก้ปัญหา ฉันคิดว่ามันเป็นสิ่งที่ดี.
Duannx

มันคือวานิลลา JS และใช้ได้กับเป้าหมายกิจกรรมที่นำออกจาก DOM (เช่นเมื่อเลือกค่าจากป๊อปอัปภายในให้ปิดป๊อปอัปทันที) +1 จากฉัน!
อลิซ

6

ขอฟังเหตุการณ์คลิกในเอกสาร ภายในฟังเหตุการณ์คุณสามารถดูวัตถุเหตุการณ์โดยเฉพาะอย่างยิ่งevent.targetเพื่อดูองค์ประกอบที่คลิก:

$(document).click(function(e){
    if ($(e.target).closest("#menuscontainer").length == 0) {
        // .closest can help you determine if the element 
        // or one of its ancestors is #menuscontainer
        console.log("hide");
    }
});

6

ฉันใช้สคริปต์ด้านล่างและใช้ jQuery

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

ด้านล่างค้นหารหัส HTML

<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>

คุณสามารถอ่านบทช่วยสอนได้ที่นี่


5
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

หากคุณคลิกที่เอกสารให้ซ่อนองค์ประกอบที่ระบุยกเว้นว่าคุณคลิกที่องค์ประกอบเดียวกันนั้น


5

โหวตขึ้นสำหรับคำตอบยอดนิยม แต่เพิ่ม

&& (e.target != $('html').get(0)) // ignore the scrollbar

ดังนั้นการคลิกบนแถบเลื่อนจะไม่ [ซ่อนหรืออะไรก็ตาม] องค์ประกอบเป้าหมายของคุณ


5

เพื่อการใช้งานที่ง่ายขึ้นและรหัสที่แสดงออกมากขึ้นฉันได้สร้างปลั๊กอิน jQuery สำหรับสิ่งนี้:

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

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

เสียบเข้าไป:

$.fn.clickOut = function (parent, fn) {
    var context = this;
    fn = (typeof parent === 'function') ? parent : fn;
    parent = (parent instanceof jQuery) ? parent : $(document);

    context.each(function () {
        var that = this;
        parent.on('click', function (e) {
            var clicked = $(e.target);
            if (!clicked.is(that) && !clicked.parents().is(that)) {
                if (typeof fn === 'function') {
                    fn.call(that, clicked);
                }
            }
        });

    });
    return context;
};

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

ใช้อย่างนั้น:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.