ตัวจัดการเหตุการณ์ Javascript พร้อมพารามิเตอร์


91

ฉันต้องการสร้าง EventHandler ที่ส่งผ่านเหตุการณ์และพารามิเตอร์บางอย่าง ปัญหาคือฟังก์ชันไม่ได้รับองค์ประกอบ นี่คือตัวอย่าง:

doClick = function(func){
    var elem = .. // the element where it is all about
    elem.onclick = function(e){
        func(e, elem);
    }
}
doClick(function(e, element){
    // do stuff with element and the event
});

ต้องกำหนด 'elem' นอกฟังก์ชันที่ไม่ระบุตัวตน ฉันจะรับองค์ประกอบที่ส่งผ่านเพื่อใช้ภายในฟังก์ชันนิรนามได้อย่างไร มีวิธีทำไหม?

แล้ว addEventListener ล่ะ? ดูเหมือนว่าฉันจะไม่สามารถส่งต่อเหตุการณ์ผ่าน addEventListener ได้เลยใช่ไหม

อัปเดต

ดูเหมือนว่าฉันจะแก้ไขปัญหาด้วย "this"

doClick = function(func){
    var that = this;
    this.element.onclick = function(e){
        func(e, that);
    }
}

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

addEventListener

แต่ฉันสงสัยเกี่ยวกับ addEventListener:

function doClick(elem, func){
    element.addEventListener('click', func(event, elem), false);
}

2
คุณมีองค์ประกอบองค์ประกอบองค์ประกอบที่เกี่ยวข้องกับคำถามของคุณ คุณจะสับสนน้อยลงได้ไหม
mihai

ฉันขอโทษฉันพยายามสร้างตัวอย่างสถานการณ์ของฉัน แต่มันไม่ได้ผลฉันอัปเดตรหัส
sebas2day

คุณกำลังมองหาe.target/ e.currentTarget?
Rolf

1
วิธีการส่งพารามิเตอร์หากฉันใช้สิ่งนี้: ok.addEventListener ("click", this.changeText.bind (this));
Alberto Acuña

คำตอบ:


103

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

function addClickHandler(elem, arg1, arg2) {
    elem.addEventListener('click', function(e) {
        // in the event handler function here, you can directly refer
        // to arg1 and arg2 from the parent function arguments
    }, false);
}

ขึ้นอยู่กับสถานการณ์การเข้ารหัสที่แน่นอนของคุณคุณสามารถทำการปิดบางประเภทเพื่อรักษาการเข้าถึงตัวแปรให้คุณได้

จากความคิดเห็นของคุณหากสิ่งที่คุณพยายามทำให้สำเร็จคือ:

element.addEventListener('click', func(event, this.elements[i]))

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

element.addEventListener('click', (function(passedInElement) {
    return function(e) {func(e, passedInElement); };
}) (this.elements[i]), false);

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของ IIFE โปรดดูข้อมูลอ้างอิงอื่น ๆ เหล่านี้:

โค้ดการตัด Javascript ภายในฟังก์ชันที่ไม่ระบุชื่อ

ทันทีเรียกใช้นิพจน์ฟังก์ชัน (IIFE) ใน JavaScript - ผ่าน jQuery

อะไรคือกรณีการใช้งานที่ดีสำหรับ JavaScript ที่เรียกใช้ฟังก์ชันนิรนามด้วยตนเอง

เวอร์ชันล่าสุดนี้อาจจะง่ายกว่าที่จะเห็นว่ามันทำอะไรเช่นนี้:

// return our event handler while capturing an argument in the closure
function handleEvent(passedInElement) {
    return function(e) {
        func(e, passedInElement); 
    };
}

element.addEventListener('click', handleEvent(this.elements[i]));

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

elem.addEventListener('click', function(a1, a2, e) {
    // inside the event handler, you have access to both your arguments
    // and the event object that the event handler passes
}.bind(elem, arg1, arg2));

ใช่นี่เกือบจะเป็นสิ่งที่ฉันพยายามจะทำ แต่จะเกิดอะไรขึ้นถ้าฟังก์ชันที่ไม่ระบุชื่อถูกส่งผ่านเป็นพารามิเตอร์โดย addClickHandler และคุณต้องการให้ฟังก์ชันนี้มีพารามิเตอร์บางอย่างกับเหตุการณ์เช่น: element.addEventListener ('click', func (event, this.elements [i]));
sebas2day

5
@ sebas2day - element.addEventListener('click', func(event, this.elements[i]))คุณไม่สามารถทำ Javascript ไม่ทำงานในลักษณะนั้น มีวิธีอื่นในการใช้การปิด แต่คุณไม่สามารถทำได้อย่างที่เขียนไว้ addEventListener เรียกมันว่าเป็นการเรียกกลับด้วยอาร์กิวเมนต์เดียว (เหตุการณ์) และคุณไม่สามารถเปลี่ยนแปลงได้ แต่อย่างใด
jfriend00

ฉันได้เพิ่มอีกตัวอย่างของการทำเช่นนี้ต่อท้ายคำตอบของฉัน
jfriend00

@ jfriend00 สิ่งที่ทำได้คือใช้วิธีผูก function eventFunction (arg, evt) { console.log(arg[0],arg[1],arg[2]) console.log(evt) } var el = document.getElementById('elementID'); el.addEventListener('click', eventFunction.bind(el,[1,2,3])) โปรดสังเกตว่าอาร์กิวเมนต์เหตุการณ์จะอยู่ในอาร์กิวเมนต์สำหรับฟังก์ชันเสมอและไม่ได้ประกาศไว้อย่างชัดเจนในวิธีการผูก
Knight Yoshi

2
@ Bosh19 - ดูสิ่งที่ฉันได้เพิ่มไว้ในตอนท้ายของคำตอบเพื่ออธิบายโครงสร้างที่คุณถาม มันเป็นนิพจน์ฟังก์ชันที่เรียกใช้ทันที (IIFE) ซึ่งโดยพื้นฐานแล้วเป็นฟังก์ชันที่ประกาศโดยไม่ระบุตัวตนซึ่งจะดำเนินการทันที เป็นทางลัดสำหรับกำหนดฟังก์ชันแล้วเรียกใช้ - มีประโยชน์เมื่อคุณต้องการให้ร่างกายอยู่ในบรรทัดและจะไม่ถูกเรียกที่อื่นดังนั้นจึงไม่จำเป็นต้องมีชื่อ
jfriend00

29

เป็นคำถามเก่า แต่เป็นคำถามธรรมดา ขอผมเพิ่มอันนี้ตรงนี้

ด้วยไวยากรณ์ของฟังก์ชันลูกศรคุณสามารถบรรลุวิธีที่กระชับมากขึ้นเนื่องจากมีการผูกศัพท์และสามารถผูกมัดได้

นิพจน์ฟังก์ชันลูกศรเป็นทางเลือกที่กระชับเชิงไวยากรณ์ให้กับนิพจน์ฟังก์ชันทั่วไปแม้ว่าจะไม่มีการเชื่อมโยงกับคำหลักนี้อาร์กิวเมนต์ super หรือ new.target ก็ตาม

const event_handler = (event, arg) => console.log(event, arg);
el.addEventListener('click', (event) => event_handler(event, 'An argument'));

หากคุณต้องการทำความสะอาดตัวฟังเหตุการณ์:

// Let's use use good old function sytax
function event_handler(event, arg) {
  console.log(event, arg);
}

// Assign the listener callback to a variable
var doClick = (event) => event_handler(event, 'An argument'); 

el.addEventListener('click', doClick);

// Do some work...

// Then later in the code, clean up
el.removeEventListener('click', doClick);

นี่คือซับเดียวที่บ้าคลั่ง:

// You can replace console.log with some other callback function
el.addEventListener('click', (event) => ((arg) => console.log(event, arg))('An argument'));

รุ่นที่ว่านอนสอนง่ายมากขึ้น : เหมาะสำหรับงานที่มีเหตุผลมากกว่า

el.addEventListener('click', (event) => ((arg) => {
  console.log(event, arg);
})('An argument'));

2
นี่ดูเหมือนจะเป็นวิธีแก้ปัญหาที่สมเหตุสมผล แต่ถ้าคุณต้องลบผู้ฟังเหตุการณ์เดียวกันล่ะ?
Dushyant Sabharwal

@SnnSnn: +1. ฉันใช้สิ่งนี้เพื่อจัดหาคุณสมบัติคลาสให้กับตัวจัดการเหตุการณ์ ฉันเพิ่งผ่านเข้าไปthisและตัวจัดการสามารถเข้าถึงสิ่งที่ต้องการได้
Robotron

1
หากความเข้าใจของฉันถูกต้องคุณจะส่งผ่านฟังก์ชันที่ไม่ระบุตัวตนเป็นตัวจัดการเหตุการณ์ของคุณ: (e) => {.... } ไม่ใช่ yourEventHandlerFunction () สิ่งนี้จะทำให้เกิดปัญหาหากคุณต้องการลบตัวจัดการเหตุการณ์ในภายหลังเนื่องจากคุณส่งผ่านฟังก์ชันที่ไม่ระบุตัวตนเพื่อเริ่มต้นด้วย ???
PowerAktar

คำตอบอธิบายวิธีลบตัวฟังเหตุการณ์อย่างชัดเจนดังนั้นจึงไม่มีปัญหา
snnsnn

9

สิ่งที่คุณสามารถลองใช้วิธีการผูกฉันคิดว่าสิ่งนี้จะบรรลุตามที่คุณขอ ถ้าไม่มีอะไรก็ยังมีประโยชน์มาก

function doClick(elem, func) {
  var diffElem = document.getElementById('some_element'); //could be the same or different element than the element in the doClick argument
  diffElem.addEventListener('click', func.bind(diffElem, elem))
}

function clickEvent(elem, evt) {
  console.log(this);
  console.log(elem); 
  // 'this' and elem can be the same thing if the first parameter 
  // of the bind method is the element the event is being attached to from the argument passed to doClick
  console.log(evt);
}

var elem = document.getElementById('elem_to_do_stuff_with');
doClick(elem, clickEvent);

2

จากการอัปเดตคำถามเดิมดูเหมือนว่าจะมีปัญหากับบริบท ("this") ขณะส่งตัวจัดการเหตุการณ์ มีการอธิบายพื้นฐานเช่นที่นี่ http://www.w3schools.com/js/js_function_invocation.asp

ตัวอย่างเวอร์ชันที่ใช้งานง่ายของคุณสามารถอ่านได้

var doClick = function(event, additionalParameter){
    // do stuff with event and this being the triggering event and caller
}

element.addEventListener('click', function(event)
{
  var additionalParameter = ...;
  doClick.call(this, event, additionalParameter );
}, false);

ดู Javascript call () & apply () vs bind ()?


1

คำตอบสั้น ๆ :

x.addEventListener("click", function(e){myfunction(e, param1, param2)});

... 

function myfunction(e, param1, param1) {
    ... 
} 

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

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

1
ไม่มันไม่ทำงาน ฉันทดสอบแล้วเหมือนที่คุณทำและคะแนนของฉันยังคงใช้ได้ ถ้า param1เป็นnoodleครั้งแรก addEventListener ที่เรียกว่าและครั้งที่สองที่คุณเรียกว่าparamเป็นcheeseแล้วเมื่อฟังเหตุการณ์clickไฟไหม้param1จะถูกตั้งค่าและไม่ได้cheese noodleดังนั้นจึงไม่ใช้การ "ปิด" เพื่อจดจำค่าที่มีอยู่ในขณะที่มีการเพิ่มผู้ฟังเหตุการณ์ มีสถานการณ์ที่ต้องเพิ่มตัวฟังเหตุการณ์หลายครั้งซึ่งไม่จำเป็นต้องมีภาพประกอบเพื่อให้คะแนนของฉันถูกต้อง
TetraDev

ฉันไม่ใช่ผู้เชี่ยวชาญและฉันไม่เคยได้ยินเรื่อง "การปิด" ในการเขียนโปรแกรมมาก่อนดังนั้นฉันจึงไม่เข้าใจว่าคุณหมายถึงอะไร
user3093134

2
ขอบคุณสำหรับการตรวจสอบฉันจะไปเยี่ยมชมลิงค์ที่คุณโพสต์ด้วยเช่นกัน นอกจากนี้ฉันต้องบอกว่าคุณเป็นคนสุภาพและซื่อสัตย์มากรักษามันไว้และมีวันที่ดี :)
user3093134

0

thisภายในdoThingsเป็นวัตถุหน้าต่าง ลองสิ่งนี้แทน:

var doThings = function (element) {
    var eventHandler = function(ev, func){
        if (element[ev] == undefined) {
            return;
        }

        element[ev] = function(e){
            func(e, element);
        }
    };

    return {
        eventHandler: eventHandler
    };
};

-2
let obj = MyObject();

elem.someEvent( function(){ obj.func(param) } );

//calls the MyObject.func, passing the param.

param ยังสามารถเป็นฟังก์ชันเรียกกลับ (ในบริบท -say- global) และสามารถเรียกกลับได้อย่างง่ายดายพร้อมกับพารามิเตอร์ที่ร้องขอ
ΔημήτρηςΒουζουναράς
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.