2013-02-27 48 views
11

Tôi đã không nhìn thấy nguồn gốc của chức năng này, nhưng tôi tự hỏi, nó hoạt động như thế này:jQuery.on() hoạt động như thế nào?

  1. Lựa chọn phần tử (s) bởi selecotrs họ
  2. đại biểu các cung cấp sự kiện xử lý đối với họ
  3. Chạy một số setInterval trên công cụ chọn đó và liên tục hủy ủy quyền và sau đó ủy quyền lại cùng một sự kiện đó trên

Hoặc có giải thích DOM thuần JavaScript?

Trả lời

15

Tôi cho rằng câu hỏi của bạn là về phiên bản Event Delegation của .on.

Trong JavaScript, hầu hết các sự kiện bubble trong hệ thống phân cấp DOM. Điều đó có nghĩa là, khi một sự kiện có thể bong bóng cháy cho một phần tử, nó sẽ phát tán lên DOM cho đến mức document.

Cân nhắc đánh dấu cơ bản này:

<div> 
    <span>1</span> 
    <span>2</span> 
</div> 

Bây giờ chúng ta áp dụng đoàn sự kiện:

$('div').on('click', 'span', fn); 

Các xử lý sự kiện được gắn hoàn toàn vào yếu tố div. Vì số span nằm trong số div, bất kỳ lần nhấp nào trong các nhịp sẽ bong bóng lên đến số div, kích hoạt trình xử lý nhấp chuột của div. Tại thời điểm đó, tất cả những gì còn lại là kiểm tra xem event.target (hay bất kỳ phần tử nào giữa mục tiêu và delegateTarget) khớp với bộ chọn mục tiêu của đại biểu.


Hãy làm cho phức tạp hơn một chút:

<div id="parent"> 
    <span>1</span> 
    <span>2 <b>another nested element inside span</b></span> 
    <p>paragraph won't fire delegated handler</p> 
</div> 

Logic cơ bản là như sau:

//attach handler to an ancestor 
document.getElementById('parent').addEventListener('click', function(e) { 
    //filter out the event target 
    if (e.target.tagName.toLowerCase() === 'span') { 
     console.log('span inside parent clicked'); 
    } 
}); 

Mặc dù ở trên sẽ không phù hợp khi event.target được lồng vào bên trong bộ lọc của bạn. Chúng ta cần một số logic lặp:

document.getElementById('parent').addEventListener('click', function(e) { 
    var failsFilter = true, 
     el = e.target; 
    while (el !== this && (failsFilter = el.tagName.toLowerCase() !== 'span') && (el = el.parentNode)); 
    if (!failsFilter) { 
     console.log('span inside parent clicked'); 
    } 
}); 

Fiddle

chỉnh sửa: Cập nhật mã để phù hợp với chỉ các yếu tố hậu duệ như jQuery .on làm.

Lưu ý: Các đoạn mã này nhằm mục đích giải thích, không được sử dụng trong thế giới thực. Trừ khi bạn không có kế hoạch để hỗ trợ IE cũ, tất nhiên. Đối với IE cũ, bạn cần phải thử nghiệm tính năng với addEventListener/attachEvent cũng như event.target || event.srcElement và có thể một số điều khác như kiểm tra xem đối tượng sự kiện có được chuyển đến hàm xử lý hay có sẵn trong phạm vi toàn cục hay không. Rất may jQuery làm tất cả những gì liền mạch đằng sau hậu trường cho chúng tôi.=]

+0

tốt đó là tất cả OK về jQuery và làm thế nào để sử dụng jQuery.on(), tôi quan tâm là nó có thể viết một đoạn mã tương tự chỉ trên JavaScript và DOM? –

+0

@Zlatan Vâng. Mặc dù không sizzle nó, bạn sẽ không có nhiều lựa chọn phái đoàn ưa thích. Tôi sẽ thêm một ví dụ JS thuần túy. –

+0

OK. Tôi nghĩ bạn có thể viết một ví dụ với: 'document.querySelector()' hoặc 'document.querySelectorAll()' :) –

2

Necromancing:
Chỉ trong trường hợp bất cứ ai cần phải thay thế JQuery on/sống với Vanilla JavaScript:

nguyên cảo:

/// attach an event handler, now or in the future, 
/// for all elements which match childselector, 
/// within the child tree of the element maching parentSelector. 
export function subscribeEvent(parentSelector: string | Element 
    , eventName: string 
    , childSelector: string 
    , eventCallback) 
{ 
    if (parentSelector == null) 
     throw new ReferenceError("Parameter parentSelector is NULL"); 

    if (childSelector == null) 
     throw new ReferenceError("Parameter childSelector is NULL"); 

    // nodeToObserve: the node that will be observed for mutations 
    let nodeToObserve: Element = <Element>parentSelector; 
    if (typeof (parentSelector) === 'string') 
     nodeToObserve = document.querySelector(<string>parentSelector); 


    let eligibleChildren: NodeListOf<Element> = nodeToObserve.querySelectorAll(childSelector); 

    for (let i = 0; i < eligibleChildren.length; ++i) 
    { 
     eligibleChildren[i].addEventListener(eventName, eventCallback, false); 
    } // Next i 

    // https://stackoverflow.com/questions/2712136/how-do-i-make-this-loop-all-children-recursively 
    function allDescendants(node: Node) 
    { 
     if (node == null) 
      return; 

     for (let i = 0; i < node.childNodes.length; i++) 
     { 
      let child = node.childNodes[i]; 
      allDescendants(child); 
     } // Next i 

     // IE 11 Polyfill 
     if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector; 

     if ((<Element>node).matches) 
     { 
      if ((<Element>node).matches(childSelector)) 
      { 
       // console.log("match"); 
       node.addEventListener(eventName, eventCallback, false); 
      } // End if ((<Element>node).matches(childSelector)) 
      // else console.log("no match"); 

     } // End if ((<Element>node).matches) 
     // else console.log("no matchfunction"); 

    } // End Function allDescendants 


    // Callback function to execute when mutations are observed 
    let callback:MutationCallback = function (mutationsList: MutationRecord[], observer: MutationObserver) 
    { 
     for (let mutation of mutationsList) 
     { 
      // console.log("mutation.type", mutation.type); 
      // console.log("mutation", mutation); 

      if (mutation.type == 'childList') 
      { 
       for (let i = 0; i < mutation.addedNodes.length; ++i) 
       { 
        let thisNode: Node = mutation.addedNodes[i]; 
        allDescendants(thisNode); 
       } // Next i 

      } // End if (mutation.type == 'childList') 
      // else if (mutation.type == 'attributes') { console.log('The ' + mutation.attributeName + ' attribute was modified.'); 

     } // Next mutation 

    }; // End Function callback 

    // Options for the observer (which mutations to observe) 
    let config = { attributes: false, childList: true, subtree: true }; 

    // Create an observer instance linked to the callback function 
    let observer = new MutationObserver(callback); 

    // Start observing the target node for configured mutations 
    observer.observe(nodeToObserve, config); 
} // End Function subscribeEvent 

JavaScript:

/// attach an event handler, now or in the future, 
    /// for all elements which match childselector, 
    /// within the child tree of the element maching parentSelector. 
    function subscribeEvent(parentSelector, eventName, childSelector, eventCallback) { 
     if (parentSelector == null) 
      throw new ReferenceError("Parameter parentSelector is NULL"); 
     if (childSelector == null) 
      throw new ReferenceError("Parameter childSelector is NULL"); 
     // nodeToObserve: the node that will be observed for mutations 
     var nodeToObserve = parentSelector; 
     if (typeof (parentSelector) === 'string') 
      nodeToObserve = document.querySelector(parentSelector); 
     var eligibleChildren = nodeToObserve.querySelectorAll(childSelector); 
     for (var i = 0; i < eligibleChildren.length; ++i) { 
      eligibleChildren[i].addEventListener(eventName, eventCallback, false); 
     } // Next i 
     // https://stackoverflow.com/questions/2712136/how-do-i-make-this-loop-all-children-recursively 
     function allDescendants(node) { 
      if (node == null) 
       return; 
      for (var i = 0; i < node.childNodes.length; i++) { 
       var child = node.childNodes[i]; 
       allDescendants(child); 
      } // Next i 
      // IE 11 Polyfill 
      if (!Element.prototype.matches) 
       Element.prototype.matches = Element.prototype.msMatchesSelector; 
      if (node.matches) { 
       if (node.matches(childSelector)) { 
        // console.log("match"); 
        node.addEventListener(eventName, eventCallback, false); 
       } // End if ((<Element>node).matches(childSelector)) 
       // else console.log("no match"); 
      } // End if ((<Element>node).matches) 
      // else console.log("no matchfunction"); 
     } // End Function allDescendants 
     // Callback function to execute when mutations are observed 
     var callback = function (mutationsList, observer) { 
      for (var _i = 0, mutationsList_1 = mutationsList; _i < mutationsList_1.length; _i++) { 
       var mutation = mutationsList_1[_i]; 
       // console.log("mutation.type", mutation.type); 
       // console.log("mutation", mutation); 
       if (mutation.type == 'childList') { 
        for (var i = 0; i < mutation.addedNodes.length; ++i) { 
         var thisNode = mutation.addedNodes[i]; 
         allDescendants(thisNode); 
        } // Next i 
       } // End if (mutation.type == 'childList') 
       // else if (mutation.type == 'attributes') { console.log('The ' + mutation.attributeName + ' attribute was modified.'); 
      } // Next mutation 
     }; // End Function callback 
     // Options for the observer (which mutations to observe) 
     var config = { attributes: false, childList: true, subtree: true }; 
     // Create an observer instance linked to the callback function 
     var observer = new MutationObserver(callback); 
     // Start observing the target node for configured mutations 
     observer.observe(nodeToObserve, config); 
    } // End Function subscribeEvent 
Các vấn đề liên quan