官术网_书友最值得收藏!

Introducing the Delegated Event Observer Pattern

Now that we have learned some advanced details about how to use the Observer Pattern using jQuery, we will get introduced to a special variation of it that fits perfectly to the web platform and provides some extra benefits. The Delegated Event Observer Pattern (or simply Delegate Observer Pattern) is often used in web development and it utilizes the bubbling feature that most events that are fired on DOM elements have. For example, when we click on a page element, the click event is immediately fired on it, and right after this it also fires on all its parent elements until it reaches the root of our HTML document. Using a slightly different overloaded version of the jQuery's $.fn.on method, we can easily create and attach observers on page elements for delegated events that are fired on specific child elements.

Note

The term "Event Delegation" describes the programming pattern where the handler of an event is not attached directly to the element of interest, but is instead attached to one of its ancestor elements.

How it simplifies our code

Reimplementing our dashboard example using the Delegated Event Observer Pattern will require us to change only the code of the included JavaScript file to the following:

$(document).ready(function() { 

    $('#categoriesSelector').change(function() { 
        var $selector = $(this); 
        var selectedIndex = +$selector.val(); 
        var $dashboardCategories = $('.dashboardCategory'); 
        var $selectedItem = $dashboardCategories.eq(selectedIndex).show(); 
        $dashboardCategories.not($selectedItem).hide(); 
    }); 

 $('.dashboardCategories').on('click', 'button', function() { 
        var $button = $(this); 
        var boxHtml = '<p class="boxsizer"><article class="box">' + 
                '<header class="boxHeader">' + 
                    $button.text() + 
                    '<button class="boxCloseButton">&#10006;' + 
                    '</button>' + 
                '</header>' + 
                'Information box regarding ' + $button.text() + 
            '</article></p>'; 
        $('.boxContainer').append(boxHtml); 
    }); 

 $('.boxContainer').on('click', '.boxCloseButton', function() { 
        $(this).closest('.boxsizer').remove(); 
    }); 

});

The most obvious difference is that the new implementation is shorter. The benefits come by defining just one observer to a common ancestor element, for each action that applies to more than one page element. For this reason, we use the $.fn.on(events, selector, handler) overload variation of the $.fn.on() method.

Specifically, we add an observer to the page element with the dashboardCategories CSS class and listen for the click events that originate from any of its <button> descendants. Similarly, we add a single observer to the boxContainer element that will be executed whenever a click event fires on any of its descendants that match the .boxCloseButton CSS selector.

Since the above observers apply not only to the elements that existed in the page at the moment they were registered, but also to any element that is added at any later point of time and matches the specified CSS selector; we are able to decouple the code that handles the clicks on the close buttons and place it in a separate observer, instead of registering a new one every time a new information box is added. As a result, the observer that adds the new information boxes in the dashboard is simpler and only has to deal with creating the HTML of the box and insert it into the dashboard, leading to a greater separation of concerns. Moreover, we no longer need to handle the registration of the observer for the close button of the hint box in a separate piece of code.

Compare the memory usage benefits

We will now compare the difference in memory usage when using the $.fn.on() method with the simple and Delegated Event Observer Pattern variation. To achieve this we will open the two implementations of our dashboard example and compare their memory usage on Chrome. To open Chrome's developer tools, just press F12 and then navigate to the Timeline tab. We press the "record" button in the Chrome's Timeline tab and then press each category item button 10 times, resulting in the addition of 120 information boxes to our dashboard. After adding all the boxes, we end up with 121 open boxes in total, since the hint box will still be open and then stop the timeline recording.

The results in the timeline for our initial Observer Pattern implementation will look as follows:

Repeating the same process for the Delegated Event Observer Pattern implementation will give a smoother timeline, revealing less object allocations and Garbage Collections, as follows:

As you can see in the preceding images, we end up with 1192 page elements in both cases, but in the first implementation we are using 134 event listeners, as compared to the implementation with event delegation where we initially created three event listeners and never actually added another.

Finally, as you can see from the blue line in the graph, the memory consumption of the delegate version stayed relatively the same, adding up to just around 200 KB. On the other hand, in the original implementation, the heap size increased more than five times, gaining more than 1 MB of increase.

Adding so many elements may not be an actual use case, but the dashboard will probably not be the only dynamic part of your page. As a result, in a relatively complex web page, we could get similar improvements if we reimplemented every applicable part of it using the Delegated Event Observer Pattern variant.

主站蜘蛛池模板: 南岸区| 铅山县| 舞钢市| 廊坊市| 平度市| 徐汇区| 阿巴嘎旗| 太原市| 东乡| 务川| 虞城县| 阿拉善右旗| 长武县| 永平县| 阳谷县| 治多县| 崇义县| 南和县| 临沂市| 策勒县| 岳普湖县| 永新县| 秭归县| 永德县| 平顺县| 通渭县| 北川| 遂昌县| 县级市| 乌拉特前旗| 黄梅县| 正安县| 宁陕县| 治县。| 平原县| 南安市| 金湖县| 永嘉县| 厦门市| 襄汾县| 富川|