ตรวจพบเหตุการณ์คลิกบนองค์ประกอบหลอกเท่านั้น


213

รหัสของฉันคือ:

p {
    position: relative;
    background-color: blue;
}

p:before {
    content: '';
    position: absolute;
    left:100%;
    width: 10px;
    height: 100%;
    background-color: red;
}

โปรดดูซอนี้: http://jsfiddle.net/ZWw3Z/5/

ฉันต้องการทริกเกอร์เหตุการณ์คลิกบนองค์ประกอบหลอก (บิตสีแดง) เท่านั้น นั่นคือฉันไม่ต้องการให้เหตุการณ์การคลิกถูกกระตุ้นบนบิตสีน้ำเงิน


1
วิธีการเกี่ยวกับการตรวจสอบ pos คลิกแล้ว? stackoverflow.com/questions/3234977/…

โปรดอ่านของฉันวิธีการแก้ปัญหาที่โพสต์ก่อนหน้านี้ที่นี่
Amr

อาจจะเป็นสิ่งที่ดีแก้ปัญหาที่นี่
Reza Mamun

คำตอบ:


218

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

หากคุณต้องมีตัวจัดการคลิกในพื้นที่สีแดงเท่านั้นคุณต้องสร้างองค์ประกอบลูกเช่น a spanวางไว้หลัง<p>แท็กเปิดใช้สไตล์เพื่อp spanแทนที่p:beforeและผูกกับมัน


108
ดูเหมือนว่าในเบราว์เซอร์ที่ทันสมัยนี้เป็นไปได้ด้วยpointer-eventsค่าที่แตกต่างกันสำหรับองค์ประกอบของตัวเองและองค์ประกอบหลอก: jsfiddle.net/ZWw3Z/70
Ilya Streltsyn

5
@Ilya Streltsyn น่าอัศจรรย์ - ไม่ใช่คำตอบที่ 'ถูกต้อง' (จะไม่ทำงานถ้าคุณต้องการคลิกที่องค์ประกอบ) แต่ใช้งานได้ดีกับปุ่ม 'ปิด' บน divs
RozzA

pointer-eventsดูเหมือนจะไม่ทำเคล็ดลับสำหรับ IE9 ตาม jsfiddel ที่ Ilya โพสต์
user393274

@ user393274: IE9 ไม่รองรับตัวชี้เหตุการณ์นอก SVG
BoltClock

1
@theyuv ไม่บนหน้านั้นสามารถคลิกทั้งบรรทัดได้ทั้งทางด้านซ้ายและด้านขวาจากลิงค์ มีเพียงลิงก์เท่านั้นที่มีตัวจัดการการคลิกที่แตกต่างกันดังนั้นการคลิกจะไม่ทำให้ทรีย่อยพับ / แฉ
Ilya Streltsyn

131

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

ตัวอย่างนี้ตรวจสอบองค์ประกอบทางด้านขวาเท่านั้น แต่ควรใช้ในกรณีของคุณ

span = document.querySelector('span');

span.addEventListener('click', function (e) {
    if (e.offsetX > span.offsetWidth) {
        span.className = 'c2';
    } else {
        span.className = 'c1';
    }
});
div { margin: 20px; }
span:after { content: 'AFTER'; position: absolute; }

span.c1 { background: yellow; }
span.c2:after { background: yellow; }
<div><span>ELEMENT</span></div>

JSFiddle


1
ไม่ทำงานสำหรับฉันข้อความจะถูกเน้นเมื่อฉันคลิกที่ใด การใช้ Firefox ถ้าสำคัญ (ไม่ควร)
Flater

2
สำหรับ Firefox: jsfiddle.net/wC2p7/16 หากทำงานกับองค์ประกอบที่ซ้อนกันคุณอาจต้องคำนวณค่าชดเชยที่ซ้อนกันตามความเหมาะสม (เช่น jQuery: $ (e.target) .offset (). ซ้าย)
bebbi

1
ขอบคุณผู้ชายที่ยอดเยี่ยมมันใช้งานได้ แต่ใน Firefox และเบราว์เซอร์ที่เป็นไปตามมาตรฐานอื่น ๆ สำหรับการทดสอบว่าต้องวางเมาส์ไว้เหนือ:afterคุณต้องตรวจสอบหรือไม่if (e.clientX > span.offsetLeft + span.offsetHeight)เนื่องจากเบราว์เซอร์เหล่านี้ไม่มีe.offsetXคุณสมบัติ ซอ: jsfiddle.net/Noitidart/wC2p7/18
Noitidart

5
แม้จะเย็นกว่านี้ถ้า jQuery ตัดสินใจที่จะสนับสนุน$('a:after').on('click', fn)แต่ฉันไม่เห็นสิ่งที่เกิดขึ้นจริง: p
Linus Unnebäck

25
สิ่งนี้จะไม่ทำงานหาก::beforeหรือ::afterอยู่ภายในองค์ประกอบ
laconbass

74

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

p {
    position: relative;
    background-color: blue;
    color:#ffffff;
    padding:0px 10px;
    pointer-events:none;
}
p::before {
    content: attr(data-before);
    margin-left:-10px;
    margin-right:10px;
    position: relative;
    background-color: red;
    padding:0px 10px;
    pointer-events:auto;
}

เมื่อเป้าหมายของกิจกรรมคือองค์ประกอบ "p" ของคุณคุณจะรู้ว่ามันคือ "p: before"

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

p span {
    background:#393;
    padding:0px 10px;
    pointer-events:auto;
}

เป้าหมายเหตุการณ์ตอนนี้ทั้งองค์ประกอบ "span" และ "p: before"

ตัวอย่างที่ไม่มี jquery: http://jsfiddle.net/2nsptvcu/

ตัวอย่างด้วย jquery: http://jsfiddle.net/0vygmnnb/

นี่คือรายการเบราว์เซอร์ที่สนับสนุนตัวชี้เหตุการณ์: http://caniuse.com/#feat=pointer-events


ฉันไม่รู้ว่าทำไมโหนดแม่คืออะไร คุณกำลังบอกว่าถ้าฉันคลิกเหตุการณ์ในสิ่งที่ชอบ. box: ก่อนหน้านี้. box นั้นไม่สามารถตรวจพบการคลิกใด ๆ ?
ท่านที่เคารพมากที่สุด

นั่นเป็นความจริงบางส่วน: หาก CSS ของคุณมีอะไรบางอย่างเช่น. box {pointer-events: none;} .box: ก่อนที่ {pointer-events: auto;} ดังนั้นองค์ประกอบเดียวที่ยังคงสนับสนุนเหตุการณ์คือ p: before โปรดจำไว้ว่า js จะให้องค์ประกอบ. box หลักแก่คุณตั้งแต่. box: ก่อนหน้านี้จะไม่ได้ใช้งานจริงใน The DOM
Fasoeu

9

คำตอบของฉันจะใช้ได้กับทุกคนที่ต้องการคลิกพื้นที่ที่ชัดเจนของหน้า สิ่งนี้ใช้ได้กับฉันในตำแหน่งที่แน่นอนของฉัน: หลังจาก

ขอบคุณบทความนี้ฉันรู้ (กับ jQuery) ฉันสามารถใช้e.pageYและe.pageXแทนที่จะกังวลe.offsetY/Xและe.clientY/Xปัญหาระหว่างเบราว์เซอร์

จากการลองผิดลองถูกฉันเริ่มใช้ clientX และ clientY พิกัดในวัตถุเหตุการณ์ jQuery พิกัดเหล่านี้ให้ออฟเซ็ต X และ Y ของเมาส์เทียบกับมุมบนซ้ายของพอร์ตมุมมองของเบราว์เซอร์ ขณะที่ฉันอ่าน jQuery 1.4 คู่มืออ้างอิงโดย Karl Swedberg และ Jonathan Chaffer อย่างไรก็ตามฉันเห็นว่าพวกเขามักจะอ้างถึงพิกัด pageX และ pageY หลังจากตรวจสอบเอกสาร jQuery ที่อัปเดตแล้วฉันเห็นว่าสิ่งเหล่านี้เป็นพิกัดที่ได้มาตรฐานโดย jQuery และฉันเห็นว่าพวกเขาให้ออฟเซ็ต X และ Y ของเมาส์เทียบกับเอกสารทั้งหมด (ไม่ใช่แค่พอร์ตดู)

ผมชอบevent.pageYความคิดเพราะมันก็จะเหมือนกันเพราะมันเป็นอะไรที่สัมพันธ์กับเอกสาร ฉันสามารถเปรียบเทียบกับของฉัน: องค์ประกอบแม่ของหลังจากใช้ offset () ซึ่งส่งกลับ X และ Y ของมันยังสัมพันธ์กับเอกสาร

ดังนั้นฉันสามารถหาช่วงของภูมิภาค "คลิกได้" ในหน้าทั้งหมดที่ไม่เคยเปลี่ยนแปลง


นี่คือฉันสาธิต codepen


หรือถ้าขี้เกียจเกินไปสำหรับ codepen นี่คือ JS:

* ฉันใส่ใจเฉพาะค่า Y สำหรับตัวอย่างของฉัน

var box = $('.box');
// clickable range - never changes
var max = box.offset().top + box.outerHeight();
var min = max - 30; // 30 is the height of the :after

var checkRange = function(y) {
  return (y >= min && y <= max);
}

box.click(function(e){
  if ( checkRange(e.pageY) ) {
    // do click action
    box.toggleClass('toggle');
  }
});


6

คำตอบสั้น ๆ :

ฉันทำมัน ฉันเขียนฟังก์ชันสำหรับการใช้งานแบบไดนามิกสำหรับคนตัวเล็ก ๆ ที่นั่น ...

ตัวอย่างการทำงานที่แสดงบนหน้า

ตัวอย่างการทำงานการบันทึกไปยังคอนโซล

คำตอบยาว:

... ยังทำอยู่

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

การใช้งาน:

//some element selector and a click event...plain js works here too
$("div").click(function() {
    //returns an object {before: true/false, after: true/false}
    psuedoClick(this);

    //returns true/false
    psuedoClick(this).before;

    //returns true/false
    psuedoClick(this).after;

});

มันทำงานอย่างไร:

มันคว้าตำแหน่งความสูงความกว้างด้านบนและด้านซ้าย (ขึ้นอยู่กับตำแหน่งที่อยู่ห่างจากขอบหน้าต่าง) ขององค์ประกอบหลักและคว้าความสูงความกว้างด้านบนและตำแหน่งด้านซ้าย (ขึ้นอยู่กับขอบของภาชนะบรรจุหลัก ) และเปรียบเทียบค่าเหล่านั้นเพื่อกำหนดตำแหน่งที่องค์ประกอบ psuedo อยู่บนหน้าจอ

จากนั้นจะเปรียบเทียบตำแหน่งของเมาส์ ตราบใดที่เมาส์อยู่ในช่วงตัวแปรที่สร้างขึ้นใหม่มันจะคืนค่าเป็นจริง

บันทึก:

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

รหัส:

function psuedoClick(parentElem) {

    var beforeClicked,
      afterClicked;

  var parentLeft = parseInt(parentElem.getBoundingClientRect().left, 10),
      parentTop = parseInt(parentElem.getBoundingClientRect().top, 10);

  var parentWidth = parseInt(window.getComputedStyle(parentElem).width, 10),
      parentHeight = parseInt(window.getComputedStyle(parentElem).height, 10);

  var before = window.getComputedStyle(parentElem, ':before');

  var beforeStart = parentLeft + (parseInt(before.getPropertyValue("left"), 10)),
      beforeEnd = beforeStart + parseInt(before.width, 10);

  var beforeYStart = parentTop + (parseInt(before.getPropertyValue("top"), 10)),
      beforeYEnd = beforeYStart + parseInt(before.height, 10);

  var after = window.getComputedStyle(parentElem, ':after');

  var afterStart = parentLeft + (parseInt(after.getPropertyValue("left"), 10)),
      afterEnd = afterStart + parseInt(after.width, 10);

  var afterYStart = parentTop + (parseInt(after.getPropertyValue("top"), 10)),
      afterYEnd = afterYStart + parseInt(after.height, 10);

  var mouseX = event.clientX,
      mouseY = event.clientY;

  beforeClicked = (mouseX >= beforeStart && mouseX <= beforeEnd && mouseY >= beforeYStart && mouseY <= beforeYEnd ? true : false);

  afterClicked = (mouseX >= afterStart && mouseX <= afterEnd && mouseY >= afterYStart && mouseY <= afterYEnd ? true : false);

  return {
    "before" : beforeClicked,
    "after"  : afterClicked

  };      

}

สนับสนุน:

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


eventถูกส่งผ่านฟังก์ชั่นจัดการเหตุการณ์ทุกครั้งที่มีเหตุการณ์ถูกยิงออก ชอบคลิกหรือพิมพ์ เมื่อเราใช้.click()ฟังก์ชั่นเรากำลังรอเหตุการณ์คลิก เราสามารถระบุและกล่าวว่า.click(function(e){...})จะทำให้เหตุการณ์เป็นแต่ก็ยังเป็นที่ยอมรับในการใช้งานเพียงแค่e event

3
psEUdo ไม่ใช่ psUEdo!
biziclop

รหัสด้านบนใช้งานไม่ได้เพราะeventไม่ได้กำหนด
Sebastian Zartner

@SebastianZartner - กิจกรรมเป็นคำหลัก เมื่อผู้ใช้ทำบางสิ่ง ฉันอธิบายความคิดเห็นทั้งสองนี้ไว้เหนือความคิดเห็นของคุณ เมื่อผู้ใช้โต้ตอบกับหน้าเว็บเหตุการณ์จะถูกปิด (โดยส่วนใหญ่) เหตุการณ์เป็นคำสำคัญที่มาจากผู้ฟังเหตุการณ์ ".click ()" หากผู้พัฒนาต้องการใช้ชื่ออื่นสำหรับเหตุการณ์พวกเขาสามารถตั้งค่าในฟังเหตุการณ์เช่น ".click (e)" ซึ่งเป็นวิธีที่พบเห็นได้ทั่วไป อย่างไรก็ตาม .... แม้ว่าฉันจะมีลิงค์ที่ทำงานอยู่ด้านบน แต่ฉันก็จะมีลิงค์ที่ชัดเจนกว่าและไม่ได้ล็อกเข้าสู่คอนโซล แต่กลับไปยังหน้าสำหรับผู้ที่ต้องการความช่วยเหลือ

ฉันควรจะกล่าวว่ามันไม่ทำงานใน Firefox เพราะมีeventไม่ได้กำหนด มันใช้งานได้ใน Chrome และ Edge
Sebastian Zartner

2

เพิ่มเงื่อนไขในเหตุการณ์คลิกเพื่อ จำกัด พื้นที่ที่สามารถคลิกได้

    $('#thing').click(function(e) {
       if (e.clientX > $(this).offset().left + 90 &&
             e.clientY < $(this).offset().top + 10) {
                 // action when clicking on after-element
                 // your code here
       }
     });

การสาธิต


1

นี่คือคำตอบที่แก้ไขโดยFasoeuพร้อม CSS3 และ JS ES6 ล่าสุด

แก้ไขตัวอย่างโดยไม่ใช้ JQuery

ตัวอย่างรหัสที่สั้นที่สุด:

<p><span>Some text</span></p>
p {
    position: relative;
    pointer-events: none;
}
p::before {
    content: "";
    position: absolute;
    pointer-events: auto;
}
p span {
    display: contents;
    pointer-events: auto;
}
const all_p = Array.from(document.querySelectorAll('p'));

for (let p of all_p) {
    p.addEventListener("click", listener, false);
};

คำอธิบาย:

pointer-eventsการตรวจสอบการควบคุมของเหตุการณ์ลบที่ได้รับจากเหตุการณ์เป้าหมาย แต่ยังได้รับจากการหลอกองค์ประกอบทำให้เป็นไปได้ที่จะคลิก::beforeและ::afterและคุณก็จะรู้ว่าสิ่งที่คุณกำลังคลิกที่องค์ประกอบหลอก แต่ถ้าคุณยังคงต้องคลิกที่คุณใส่เนื้อหาทั้งหมด ในองค์ประกอบที่ซ้อนกัน ( spanในตัวอย่าง) แต่เพราะเราไม่ต้องการที่จะใช้รูปแบบใด ๆ เพิ่มเติมdisplay: contents;จะกลายเป็นโซลูชั่นที่มีประโยชน์มากและจะได้รับการสนับสนุนจากเบราว์เซอร์ส่วนใหญ่ pointer-events: none;ดังที่ได้กล่าวแล้วในการโพสต์ต้นฉบับยังกันอย่างแพร่หลายสนับสนุน

ส่วน JavaScript ยังใช้กันอย่างแพร่หลายArray.fromและfor...ofอย่างไรก็ตามพวกเขาไม่จำเป็นต้องใช้ในรหัส


0

คำตอบเหล่านี้ไม่น่าเชื่อถือและฉันจะไม่เชื่อถือได้มากขึ้น

ถ้าคุณเข้าไปในสถานการณ์ที่โชคดีที่องค์ประกอบที่คุณพยายามคลิกนั้นไม่มีช่องว่างภายใน (เช่นพื้นที่ทั้งหมดขององค์ประกอบภายในนั้นถูกครอบคลุมโดยองค์ประกอบย่อยทั้งหมด) สามารถตรวจสอบเป้าหมายของเหตุการณ์คลิกกับคอนเทนเนอร์ได้ ถ้ามันตรงกันนั่นหมายความว่าคุณได้คลิก a: before หรือ: after element

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


-2

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

ในไฟล์ html เพิ่มส่วนนี้

<div class="arrow">
</div>

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

p div.arrow {
content: '';
position: absolute;
left:100%;
width: 10px;
height: 100%;
background-color: red;

} หวังว่ามันจะช่วยคุณ

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