2014-05-07 13 views
8

Cách tốt nhất (nhanh nhất/thích hợp) để làm đại biểu sự kiện trong vanilla js là gì?Đoàn đại biểu tổ chức sự kiện Vanilla JavaScript

Ví dụ nếu tôi có điều này trong jQuery:

$('#main').on('click', '.focused', function(){ 
    settingsPanel(); 
}); 

Làm thế nào tôi có thể dịch đó để vani js? Có lẽ với .addEventListener()

Con đường tôi có thể nghĩ đến việc này là:

document.getElementById('main').addEventListener('click', dothis); 
function dothis(){ 
    // now in jQuery 
    $(this).children().each(function(){ 
     if($(this).is('.focused') settingsPanel(); 
    }); 
} 

Nhưng điều đó dường như không hiệu quả đặc biệt là nếu #main có nhiều trẻ em.

Đây có phải là cách thích hợp để làm điều đó không?

document.getElementById('main').addEventListener('click', doThis); 
function doThis(event){ 
    if($(event.target).is('.focused') || $(event.target).parents().is('.focused') settingsPanel(); 
} 
+4

Bắt đầu với 'event.target', xem nó có khớp với bộ chọn hay không và nếu có, hãy gọi hàm. Sau đó lặp lại thông qua tổ tiên của nó và làm tương tự cho đến khi bạn nhận được phần tử 'this'. Bạn có thể sử dụng 'node.matchesSelector()' để làm thử nghiệm, mặc dù trong một số trình duyệt nó được thực hiện với một cờ đặc trưng cho trình duyệt, vì vậy bạn cần phải bọc nó trong một hàm, hoặc đặt phương thức cờ trên 'Element. prototype.matchesSelector' –

+1

Bạn nên đăng câu trả lời đó làm câu trả lời. –

+0

@EvanTrimboli: Tôi không thích đăng câu trả lời. Tôi sẽ để người khác lấy đại diện. Cảm thấy tự do nếu bạn muốn. –

Trả lời

9

I've come up with a simple solution mà dường như làm việc khá tốt (di sản IE hỗ trợ mặc dù). Ở đây chúng ta mở rộng EventTarget 's nguyên mẫu để cung cấp một phương pháp delegateEventListener trong đó hoạt động bằng cách sử dụng cú pháp sau:

EventTarget.delegateEventListener(string event, string toFind, function fn) 

Tôi đã tạo một khá phức tạp fiddle để chứng minh nó trong hành động, nơi mà chúng tôi ủy thác tất cả các sự kiện cho các yếu tố màu xanh lá cây. Dừng tuyên truyền tiếp tục hoạt động và bạn có thể truy cập những gì cần là event.currentTarget thông qua this (như với jQuery).

Dưới đây là giải pháp đầy đủ:

(function(document, EventTarget) { 
    var elementProto = window.Element.prototype, 
     matchesFn = elementProto.matches; 

    /* Check various vendor-prefixed versions of Element.matches */ 
    if(!matchesFn) { 
    ['webkit', 'ms', 'moz'].some(function(prefix) { 
     var prefixedFn = prefix + 'MatchesSelector'; 
     if(elementProto.hasOwnProperty(prefixedFn)) { 
     matchesFn = elementProto[prefixedFn]; 
     return true; 
     } 
    }); 
    } 

    /* Traverse DOM from event target up to parent, searching for selector */ 
    function passedThrough(event, selector, stopAt) { 
    var currentNode = event.target; 

    while(true) { 
     if(matchesFn.call(currentNode, selector)) { 
     return currentNode; 
     } 
     else if(currentNode != stopAt && currentNode != document.body) { 
     currentNode = currentNode.parentNode; 
     } 
     else { 
     return false; 
     } 
    } 
    } 

    /* Extend the EventTarget prototype to add a delegateEventListener() event */ 
    EventTarget.prototype.delegateEventListener = function(eName, toFind, fn) { 
    this.addEventListener(eName, function(event) { 
     var found = passedThrough(event, toFind, event.currentTarget); 

     if(found) { 
     // Execute the callback with the context set to the found element 
     // jQuery goes way further, it even has it's own event object 
     fn.call(found, event); 
     } 
    }); 
    }; 

}(window.document, window.EventTarget || window.Element)); 
+0

Thx, giải pháp tuyệt vời! – jaydoubleyou

+0

Bạn nên kiểm tra trước cho phiên bản chuẩn không được sửa đổi. – gilly3

+0

@ gilly3 Tôi cũng nên tính toán nó một lần thay vì sau đó mỗi lần. Tôi sẽ cập nhật –

0

Tôi có một giải pháp tương tự để đạt được đoàn sự kiện. Nó sử dụng các hàm Array slice, reverse, filterforEach.

  • slice chuyển đổi NodeList từ truy vấn thành mảng, phải được thực hiện trước khi được phép đảo ngược danh sách.
  • reverse đảo ngược mảng (làm cho traversion thức bắt đầu càng gần với sự kiện mục tiêu càng tốt.
  • filter kiểm tra mà các yếu tố chứa event.target.
  • forEach gọi là xử lý được cung cấp cho mỗi phần tử từ kết quả lọc càng lâu như xử lý không trả lại false.

chức năng trả về chức năng đại biểu được tạo ra, mà làm cho nó có thể để loại bỏ người nghe sau. Lưu ý rằng nguồn gốc event.stopPropagation() không n ot dừng di chuyển ngang qua validElements, vì pha sủi bọt đã đi qua đến phần tử ủy nhiệm.

function delegateEventListener(element, eventType, childSelector, handler) { 
    function delegate(event){ 
     var bubble; 
     var validElements=[].slice.call(this.querySelectorAll(childSelector)).reverse().filter(function(matchedElement){ 
      return matchedElement.contains(event.target); 
     }); 
     validElements.forEach(function(validElement){ 
      if(bubble===undefined||bubble!==false)bubble=handler.call(validElement,event); 
     }); 
    } 
    element.addEventListener(eventType,delegate); 
    return delegate; 
} 

Mặc dù nó không được khuyến khích để mở rộng nguyên mẫu có nguồn gốc, chức năng này có thể được bổ sung vào nguyên mẫu cho EventTarget (hoặc Node trong IE).Khi làm như vậy, hãy thay thế element bằng this trong hàm và xóa thông số tương ứng (EventTarget.prototype.delegateEventListener = function(eventType, childSelector, handler){...}).

Các vấn đề liên quan