กำหนดองค์ประกอบที่ตัวชี้เมาส์อยู่ด้านบนใน JavaScript


111

ฉันต้องการฟังก์ชันที่บอกฉันว่าเคอร์เซอร์ของเมาส์อยู่เหนือองค์ประกอบใด

ตัวอย่างเช่นหากเมาส์ของผู้ใช้อยู่เหนือพื้นที่ข้อความนี้ (พร้อม id wmd-input) การโทรwindow.which_element_is_the_mouse_on()จะเทียบเท่ากับฟังก์ชัน$("#wmd-input")จะเป็นหน้าที่เทียบเท่า

คำตอบ:


149

การสาธิต

มีฟังก์ชั่นเด็ด ๆ ที่เรียกว่าdocument.elementFromPointซึ่งทำในสิ่งที่ดูเหมือน

สิ่งที่เราต้องการคือหา x และ y coords ของเมาส์แล้วเรียกมันโดยใช้ค่าเหล่านั้น:

var x = event.clientX, y = event.clientY,
    elementMouseIsOver = document.elementFromPoint(x, y);

document.elementFromPoint

วัตถุเหตุการณ์ jQuery


1
@TikhonJelvis: จากตรงนี้ฉันเห็นว่ามันรองรับบน IE และ firefox ถ้าคุณได้สองสิ่งนี้คุณมักจะได้รับทั้งหมด MSDN MDN
qwertymk

1
น่ากลัว มีวิธีใดบ้างในการรับ coords ของเมาส์โดยไม่จับเหตุการณ์? (อาจไม่ใช่ฉันถือว่า) ถ้าเป็นไปไม่ได้ก็น่าเสียดายที่ฉันไม่คิดว่าวิธีนี้จะดีไปกว่าevent.targetหรืออะไรก็ตาม
Tom Lehman

1
@HoraceLoeb เป็นไปไม่ได้ที่จะได้รับ coords เมาส์โดยไม่ต้องจับเหตุการณ์ ดูคำถาม SO นี้สำหรับคำอธิบายเพิ่มเติม
Logan Besecker

2
วิธีนี้เป็นวิธีที่แปลก หากคุณจับเหตุการณ์ทำไมไม่ใช้event.target?
pmrotule

3
คำตอบไม่ได้อธิบายว่าeventคืออะไรและเป็นมาได้อย่างไร
vsync

65

ในเบราว์เซอร์รุ่นใหม่คุณสามารถทำสิ่งต่อไปนี้:

document.querySelectorAll( ":hover" );

นั่นจะทำให้คุณมีรายการ NodeList ของรายการที่เมาส์อยู่ในลำดับเอกสาร องค์ประกอบสุดท้ายใน NodeList มีความเฉพาะเจาะจงมากที่สุดแต่ละองค์ประกอบก่อนหน้าควรเป็นพ่อแม่ปู่ย่าตายายและอื่น ๆ


2
สิ่งนี้ดูเหมือนจะไม่ได้ผลในขณะที่ฉันลาก<li>ในขณะที่อยู่บน<li>องค์ประกอบอื่น ๆ
Seiyria

2
ฉันสร้างซอด้วย$(':hover')แต่โดยพื้นฐานแล้วมันก็เหมือนกัน: jsfiddle.net/pmrotule/2pks4tf6
pmrotule

3
(function(){ var q = document.querySelectorAll(":hover"); return q[q.length-1]; })()
ผู้ใช้

3
ฉันรู้สึกว่าการแสดงของเรื่องนี้แย่มาก .. การเรียกสิ่งนี้mousemoveอาจทำให้ประสิทธิภาพการทำงานแย่ลง
vsync

50

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

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

แนวทาง "โครงสร้าง" - โครงสร้าง DOM จากน้อยไปมาก

ในคำตอบของเดอร์แมนเราสามารถโทร

var elements = document.querySelectorAll(':hover');

อย่างไรก็ตามสิ่งนี้ถือว่ามีเพียงเด็ก ๆ เท่านั้นที่จะซ้อนทับบรรพบุรุษของพวกเขาซึ่งโดยปกติจะเป็นเช่นนั้น แต่ไม่เป็นความจริงโดยทั่วไปโดยเฉพาะอย่างยิ่งเมื่อจัดการกับ SVG ที่องค์ประกอบในกิ่งก้านต่างๆของต้นไม้ DOM อาจทับซ้อนกัน

แนวทาง "ภาพ" - ขึ้นอยู่กับ "ภาพ" ที่ทับซ้อนกัน

วิธีนี้ใช้document.elementFromPoint(x, y)เพื่อค้นหาองค์ประกอบที่อยู่บนสุดซ่อนไว้ชั่วคราว (เนื่องจากเรากู้คืนทันทีในบริบทเดียวกันเบราว์เซอร์จะไม่แสดงผลสิ่งนี้จริง ๆ ) จากนั้นไปหาองค์ประกอบที่อยู่บนสุดอันดับที่สอง ... ดูเหมือนแฮ็คเล็กน้อย แต่ มันจะส่งคืนสิ่งที่คุณคาดหวังเมื่อมีเช่นองค์ประกอบพี่น้องในต้นไม้ที่มารวมกัน โปรดดูโพสต์นี้เพื่อดูรายละเอียดเพิ่มเติม

function allElementsFromPoint(x, y) {
    var element, elements = [];
    var old_visibility = [];
    while (true) {
        element = document.elementFromPoint(x, y);
        if (!element || element === document.documentElement) {
            break;
        }
        elements.push(element);
        old_visibility.push(element.style.visibility);
        element.style.visibility = 'hidden'; // Temporarily hide the element (without changing the layout)
    }
    for (var k = 0; k < elements.length; k++) {
        elements[k].style.visibility = old_visibility[k];
    }
    elements.reverse();
    return elements;
}

ลองทั้งสองอย่างและตรวจสอบผลตอบแทนที่แตกต่างกัน


สิ่งนี้ช่วยฉันได้มาก ขอบคุณ @ herrlich10 น่าแปลกใจที่รหัสของคุณจัดการเคสที่มีองค์ประกอบลูกที่ซ้อนกันด้วยเช่นกัน
Vikram Deshmukh

นี่เป็นประโยชน์อย่างยิ่ง สิ่งที่ฉันกำลังมองหา ดังที่คุณกล่าวไว้ querySelectorAll ไม่ทำงานในทุกสถานการณ์
noobsharp

4
@ herrlich10 นี้ดูเหมือนว่า polyfill ที่ดีสำหรับdeveloper.mozilla.org/en-US/docs/Web/API/Document/... หาก API นั้นมีอยู่ในชุดเบราว์เซอร์ที่คุณรองรับฉันคิดว่าคุณสามารถใช้ได้
dherman

ขอบคุณสำหรับสิ่งนี้ - ใช้งานได้ดีพอ ๆ กับ expetced (แม้ว่าจะไม่มีองค์ประกอบย้อนกลับสำหรับการเข้ากันได้กับ getElementsFromPoint) แต่มันช้ากว่ามาก (18-20ms ต่อการโทรในการทดสอบของฉันใน chrome) ซึ่งทำให้ยากที่จะใช้ในสถานการณ์ที่ฉันมี ใจ (การค้นหาเป้าหมายที่ลดลงระหว่างการลากในกรณีที่ใช้วิธีการขับเคลื่อนเหตุการณ์พื้นฐานกว่านั้นไม่สามารถทำได้)
jsdw

1
ดูDocument.elementsFromPoint(x, y) stackoverflow.com/a/31805883/1059828
Karl Adler

21

elementFromPoint()ได้รับเฉพาะองค์ประกอบแรกในโครงสร้าง DOM ซึ่งส่วนใหญ่ไม่เพียงพอสำหรับความต้องการของนักพัฒนา หากต้องการรับองค์ประกอบมากกว่าหนึ่งรายการเช่นตำแหน่งตัวชี้เมาส์ปัจจุบันนี่คือฟังก์ชันที่คุณต้องการ:

document.elementsFromPoint(x, y) . // Mind the 's' in elements

สิ่งนี้ส่งคืนอาร์เรย์ของออบเจ็กต์องค์ประกอบทั้งหมดภายใต้จุดที่กำหนด เพียงแค่ส่งค่า X และ Y ของเมาส์ไปที่ฟังก์ชันนี้

ข้อมูลเพิ่มเติมอยู่ที่นี่: DocumentOrShadowRoot.elementsFromPoint ()

สำหรับเบราว์เซอร์รุ่นเก่าที่ไม่รองรับคุณอาจใช้คำตอบนี้เป็นทางเลือก


3
ตอนนี้ได้รับการสนับสนุนอย่างดีในปี 2018
บอย

6

วางเมาส์เหนือฟองเหตุการณ์เพื่อให้คุณสามารถใส่ผู้ฟังคนเดียวบนเนื้อความและรอให้ฟองขึ้นจากนั้นคว้าevent.targetหรือevent.srcElement:

function getTarget(event) {
    var el = event.target || event.srcElement;
    return el.nodeType == 1? el : el.parentNode;
}

<body onmouseover="doSomething(getTarget(event));">

document.elementFromPoint(x, y)ในขณะที่ตัวเองใช้จัดการเหตุการณ์สากลนี้จะเป็นทรัพยากรที่มีราคาแพงกว่าฉลาดกว่า
John

6

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

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})

1
สิ่งนี้ต้องใช้เคอร์เซอร์เพื่อย้าย ใกล้ที่สุดเท่าที่จะทำได้ไม่ใช่สิ่งที่ถูกถามที่นี่
Carles Alcolea

ใช้ไม่ได้หากเลื่อนหน้า ใช้e.clientXและe.clientYแทน (ทดสอบบน Firefox 59)
youen


4
<!-- One simple solution to your problem could be like this: -->

<div>
<input type="text" id="fname" onmousemove="javascript: alert(this.id);" />
<!-- OR -->
<input type="text" id="fname" onclick="javascript: alert(this.id);" />
</div>
<!-- Both mousemove over the field & click on the field displays "fname"-->
<!-- Works fantastic in IE, FireFox, Chrome, Opera. -->
<!-- I didn't test it for Safari. -->

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

3

การสาธิต: D

เลื่อนเมาส์ของคุณในหน้าต่างข้อมูลโค้ด: D

<script>
document.addEventListener('mouseover', function (e) {
    console.log ("You are in ", e.target.tagName);
});
</script>


1

เป้าหมายของmousemoveเหตุการณ์ DOM คือองค์ประกอบ DOM ที่อยู่บนสุดใต้เคอร์เซอร์เมื่อเลื่อนเมาส์:

(function(){
    //Don't fire multiple times in a row for the same element
    var prevTarget=null;
    document.addEventListener('mousemove', function(e) {
        //This will be the top-most DOM element under cursor
        var target=e.target;
        if(target!==prevTarget){
            console.log(target);
            prevTarget=target;
        }
    });
})();

คล้ายกับโซลูชันของ @Philip Walton แต่ไม่ต้องใช้ jQuery หรือ setInterval


1

คุณสามารถใช้ตัวเลือกนี้เพื่อบ่อนทำลายวัตถุจากนั้นจัดการเป็นวัตถุ jQuery:

$(':hover').last();

4
ยินดีต้อนรับสู่ Stack Overflow! โปรดเพิ่มคำอธิบายว่าเหตุใดรหัสนี้จึงช่วย OP สิ่งนี้จะช่วยให้คำตอบที่ผู้ชมในอนาคตสามารถเรียนรู้ได้ ดูวิธีตอบสำหรับข้อมูลเพิ่มเติม ยัง "บ่อนทำลาย"?
ลิงนอกรีต

"การบ่อนทำลายวัตถุ"คืออะไร ? คุณหมายถึง"วัตถุที่อยู่ใต้เมาส์"หรือไม่? หรืออย่างอื่น?
Peter Mortensen

0

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

const wrapper = document.getElementById('wrapper') //parent element
const position = document.getElementById("displaySelection")

wrapper.addEventListener('mousemove', function(e) {
  let elementPointed = document.elementFromPoint(e.clientX, e.clientY)

  console.log(elementPointed)
});

สาธิตเกี่ยวกับ CodePen


0

ให้ฉันเริ่มด้วยการบอกว่าฉันไม่แนะนำให้ใช้วิธีที่ฉันกำลังจะแนะนำ มันมากดีกว่าที่จะใช้พัฒนาเหตุการณ์ขับเคลื่อนและผูกกิจกรรมเฉพาะองค์ประกอบที่คุณสนใจในการรู้หรือไม่ว่าเมาส์จะเป็นไปด้วยmouseover, mouseout, mouseenter,mouseleaveฯลฯ

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

คุณสามารถทำสิ่งนี้ได้:

window.which_element_is_the_mouse_on = (function() {

    var currentElement;

    $("body *").on('mouseover', function(e) {
        if(e.target === e.currentTarget) {
            currentElement = this;
        }
    });

    return function() {
        console.log(currentElement);
    }
}());

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

นี่คือการสาธิตที่ใช้งานได้ซึ่งเรียกใช้window.which_element_is_the_mouse_onทุก ๆ วินาทีและบันทึกองค์ประกอบที่เมาส์อยู่ในคอนโซล

http://jsfiddle.net/LWFpJ/1/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.