588

I have a huge jQuery application, and I'm using the below two methods for click events.

First method

HTML

<div id="myDiv">Some Content</div>

jQuery

$('#myDiv').click(function(){
    //Some code
});

Second method

HTML

<div id="myDiv" onClick="divFunction()">Some Content</div>

JavaScript function call

function divFunction(){
    //Some code
}

I use either the first or second method in my application. Which one is better? Better for performance? And standard?

Kamil Kiełczewski
  • 53,729
  • 20
  • 259
  • 241
Techie
  • 42,101
  • 38
  • 144
  • 232
  • 9
    You can learn about the various ways to attach event handlers and their advantages/disadvantages here: http://www.quirksmode.org/js/introevents.html. jQuery is just a nice wrapper for advanced event registration. – Felix Kling Sep 27 '12 at 18:03
  • 13
    Remember to put the click function inside the $(document).ready(function(). – joan16v Mar 17 '14 at 07:39

17 Answers17

586

Using $('#myDiv').click(function(){ is better as it follows standard event registration model. (jQuery internally uses addEventListener and attachEvent).

Basically registering an event in modern way is the unobtrusive way of handling events. Also to register more than one event listener for the target you can call addEventListener() for the same target.

var myEl = document.getElementById('myelement');

myEl.addEventListener('click', function() {
    alert('Hello world');
}, false);

myEl.addEventListener('click', function() {
    alert('Hello world again!!!');
}, false);

http://jsfiddle.net/aj55x/1/

Why use addEventListener? (From MDN)

addEventListener is the way to register an event listener as specified in W3C DOM. Its benefits are as follows:

  • It allows adding more than a single handler for an event. This is particularly useful for DHTML libraries or Mozilla extensions that need to work well even if other libraries/extensions are used.
  • It gives you finer-grained control of the phase when the listener gets activated (capturing vs. bubbling)
  • It works on any DOM element, not just HTML elements.

More about Modern event registration -> http://www.quirksmode.org/js/events_advanced.html

Other methods such as setting the HTML attributes, example:

<button onclick="alert('Hello world!')">

Or DOM element properties, example:

myEl.onclick = function(event){alert('Hello world');}; 

are old and they can be over written easily.

HTML attribute should be avoided as It makes the markup bigger and less readable. Concerns of content/structure and behavior are not well-separated, making a bug harder to find.

The problem with the DOM element properties method is that only one event handler can be bound to an element per event.

More about Traditional event handling -> http://www.quirksmode.org/js/events_tradmod.html

MDN Reference: https://developer.mozilla.org/en-US/docs/DOM/event

Denis Bubnov
  • 1,985
  • 4
  • 23
  • 47
Selvakumar Arumugam
  • 76,636
  • 14
  • 118
  • 132
  • 13
    Lets say you run the `$('#myDiv').click(function(){` code first, then you generate 20 rows of HTML dynamically from JavaScript and each row has a button on it that when clicked the JavaScript is required to execute that same function. If you do that first then it won't work as the event handler has been added before the HTML has been generated. It would seem easier to just throw in the `onclick="functionName()"` into the dynamically generated HTML then the button works straight away. Or do you know of a more elegant solution for this situation? – zuallauz Sep 27 '12 at 21:28
  • 17
    @zuallauz for that case jQuery offers `.delegate()` function. It will attach event to any element that will appear in the future on the site. – Zefiryn Sep 27 '12 at 22:54
  • @Zefiryn Thanks that function sounds great, I'll be using that in future! – zuallauz Sep 27 '12 at 23:38
  • 2
    @zuallauz the jQuery `.live()` function might be an simpler solution for what you are after [http://api.jquery.com/live/](http://api.jquery.com/live/). A good article on the differences between `.bind()`, `.live()` and `.delegate()` [here](http://www.elijahmanor.com/2012/02/differences-between-jquery-bind-vs-live.html) – Simon Robb Sep 28 '12 at 00:12
  • 17
    @SimonRobb `.live` is deprecated. Use [`.delegate`](http://api.jquery.com/delegate/) for older versions or use [`.on`](http://api.jquery.com/on/) for newer jQuery versions. – Selvakumar Arumugam Sep 28 '12 at 00:27
  • 4
    @Vega, Good points. What about readability? Now you have to search all referenced JS files on the page to see all click handlers of an element by element's ID instead of searching for the function name. What's your take on this? – supertonsky Dec 20 '12 at 06:49
  • 6
    I agree with @supertonsky. I STRONGLY disagree that $('#myDiv').click(function(){ is better. In a large javascript application binding with an event becomes massively difficult to find all the references binding to that target. Is it the binding on a class, an id, a child references to a html tag? And what happens if css changes and class names you bound to need to be changed? In my experience working with others code it becomes very ugly very fast. – Jon Jun 26 '13 at 13:03
  • 2
    @Jon Your disagreement is based on bad coding practices. If the code is ugly, you should consider refactoring it.. but not disagree on a better approach just because it is hard to find reference in a ugly code. Also, It is easy to find the reference if you follow a simple rule to bind the handler only by an ID or class. Any other way such as binding using traversal methods is bad coding and you cannot blame the standard if it is poor code. – Selvakumar Arumugam Jun 26 '13 at 14:31
  • @supertonsky I believe searching by ID or class or by function name in js files is same. However it is very hard when you bind a handler using a traversal method like `$('#myTable').find('tr').click(` or `$('.someclass').parent().click(` - but those are bad coding styles. – Selvakumar Arumugam Jun 26 '13 at 17:24
  • @Jon Having multiple elements with the same ID is bad coding in general, and may have erratic behavior. Instead, for multiple items with a listener it is proper to do `jQuery('#container').on('click','.listitem', function(){...})` or in plain javascript, set `document.getElementById('container').addEventListener(...` and check `event.target.getAttribute('class')` in the listener. – NoBugs Aug 02 '14 at 00:17
  • @supertonsky At least we now have the ability to use the HTML panel's Events tab in Firebug. I know it's not exactly what you're after but nonetheless, it remains a great aid which allows you to inspect the _actual_ event listeners registered for a selected DOM node at any given time, as opposed to scanning the code base and looking at ones that _might_ be registered at some point. – WynandB Aug 28 '14 at 02:39
  • I have a trouble use this code without run it inside of ready jquery function: $(document).ready(function() { ... }); – Intacto Oct 24 '17 at 14:05
  • Although the answer is very good, nobody seems to question the purpose of onClick events, no matter the way of calling it. The purpose is to trigger an action without refreshing the page, but in 99% of the cases we need to pass one or more parameters. In that case, it really doesn't matter if you use `data-param="value"` or `onClick="function(param)"`, either way they are visible and can be overwritten. I'm using onClick, but I always check and sanitise everything before processing the data, so it doesn't matter to me fi the parameters are visible. – Lucian Minea Apr 20 '18 at 08:59
  • 2
    "HTML attribute should be avoided as It makes the markup bigger" oh yeah, but now I have to give `id` to all my elelements. Also, LOVE answers that say "should be *avoided* because its *wrong*" – Alex from Jitbit May 19 '19 at 20:05
  • "HTML attribute should be avoided as It makes the markup bigger" having this in mind will lead to spaghetti code, HTML attribute event is 100% valid. I don't mind if ID used in getting element to bind the event. but i've seen so many "senior" developer using long query with class only. The result? other developer will have a hard time searching where the functionality of the event. I believe the best practice is using anything where other developer can find the function easily, people on vue.js has written about this https://vuejs.org/v2/guide/events.html#Why-Listeners-in-HTML – izzulmakin Dec 24 '19 at 02:53
  • This is general comment. Some comments are saying ```live``` is deprecated, use ```delegate``` or ```on``` instead etc. But some of us, are still using ```live``` and ```delegate```. They don't want to upgrade. This is sad. – kishor10d Feb 25 '20 at 08:51
67

For better performance, use the native JavaScript. For faster development, use jQuery. Check the comparison in performance at jQuery vs Native Element Performance.

I've done a test in Firefox 16.0 32-bit on Windows Server 2008 R2 / 7 64-bit

$('span'); // 6,604 operations per second
document.getElementsByTagName('span'); // 10,331,708 operations/sec

For click events, check Native Browser events vs jquery trigger or jQuery vs Native Click Event Binding.

Testing in Chrome 22.0.1229.79 32-bit on Windows Server 2008 R2 / 7 64-bit

$('#jquery a').click(window.testClickListener); // 2,957 operations/second

[].forEach.call( document.querySelectorAll('#native a'), function(el) {
    el.addEventListener('click', window.testClickListener, false);
}); // 18,196 operations/second
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Marian Zburlea
  • 8,123
  • 4
  • 29
  • 37
  • 9
    jQuery should be able to run in many different environments which makes it more robuts and it's easier to write, maintain but if speed is the most important then jQuery is not the answer – Coops Aug 06 '14 at 11:04
  • Be careful when using forEach cause is not all browser compatible, i think not for IE for example. Maybe a polyfill will be useful. – khaled_webdev Nov 07 '17 at 07:22
40

From what I understand, your question is not really about whether to use jQuery or not. It's rather: Is it better to bind events inline in HTML or through event listeners?

Inline binding is deprecated. Moreover this way you can only bind one function to a certain event.

Therefore I recommend using event listeners. This way, you'll be able to bind many functions to a single event and to unbind them later if needed. Consider this pure JavaScript code:

querySelector('#myDiv').addEventListener('click', function () {
    // Some code...
});

This works in most modern browsers.

However, if you already include jQuery in your project — just use jQuery: .on or .click function.

Michał Miszczyszyn
  • 9,122
  • 2
  • 32
  • 51
  • It is possible to register multiple functions using inline HTML like
    – Kira Mar 14 '17 at 09:37
  • 2
    This way you're still registering only one "expression". For example, if `handler1` throws an error, `handler2` and `handler3` won't ever get called. Moreover, you're unable to dynamically add and remove certain fuctions from the listener. And last but not least, `handler1`, `handler2` and `handler3` have to be declared in the global scope which is a smell. – Michał Miszczyszyn Mar 14 '17 at 14:39
  • Using `try..catch` inside the functions won't work when `handler2` in undefined. FYI inline JavaScript obviously doesn't work when JS is disabled in user's browser, please do not misinform others. – Michał Miszczyszyn Mar 16 '17 at 16:20
  • I mentioned I was not sure about disabled JavaScript. I am not misinforming or deceiving others – Kira Mar 17 '17 at 04:05
  • In that case you can try the following
    – Kira Mar 17 '17 at 04:10
  • Oh, wow, it's both more readable and certainly more convenient than `addEventListener`. Keep up with the good code! – Michał Miszczyszyn Mar 19 '17 at 17:56
19

You could combine them, use jQuery to bind the function to the click

<div id="myDiv">Some Content</div>

$('#myDiv').click(divFunction);

function divFunction(){
 //some code
}
CaffGeek
  • 20,889
  • 16
  • 95
  • 176
16

Go for this as it will give you both standard and performance.

 $('#myDiv').click(function(){
      //Some code
 });

As the second method is simple JavaScript code and is faster than jQuery. But here performance will be approximately the same.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Rahul
  • 511
  • 1
  • 7
  • 21
15

$('#myDiv').click is better, because it separates JavaScript code from HTML. One must try to keep the page behaviour and structure different. This helps a lot.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
geekman
  • 2,044
  • 11
  • 16
  • 2
    This answer makes sense as people who design and write js stuff are different in most of the cases. And it feels strange to upvote this after almost 4 years since this was posted!! – LearningEveryday Apr 14 '16 at 14:44
11

Difference in works. If you use click(), you can add several functions, but if you use an attribute, only one function will be executed - the last one.

DEMO

HTML

<span id="JQueryClick">Click #JQuery</span> </br>
<span id="JQueryAttrClick">Click #Attr</span> </br>

JavaScript

$('#JQueryClick').click(function(){alert('1')})
$('#JQueryClick').click(function(){alert('2')})

$('#JQueryAttrClick').attr('onClick'," alert('1')" ) //This doesn't work
$('#JQueryAttrClick').attr('onClick'," alert('2')" )

If we are talking about performance, in any case directly using is always faster, but using of an attribute, you will be able to assign only one function.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Anton Baksheiev
  • 2,151
  • 2
  • 12
  • 14
  • We can execute more than one functions using attribute like $('#JQueryAttrClick').attr('onClick'," alert('2');alert('3');alert('4')" ) – Kira Mar 14 '17 at 09:42
11

IMHO, onclick is the preferred method over .click only when the following conditions are met:

  • there are many elements on the page
  • only one event to be registered for the click event
  • You're worried about mobile performance/battery life

I formed this opinion because of the fact that the JavaScript engines on mobile devices are 4 to 7 times slower than their desktop counterparts which were made in the same generation. I hate it when I visit a site on my mobile device and receive jittery scrolling because the jQuery is binding all of the events at the expense of my user experience and battery life. Another recent supporting factor, although this should only be a concern with government agencies ;) , we had IE7 pop-up with a message box stating that JavaScript process is taking to long...wait or cancel process. This happened every time there were a lot of elements to bind to via jQuery.

9

Seperation of concerns is key here, and so the event binding is the generally accepted method. This is basically what a lot of the existing answers have said.

However don't throw away the idea of declarative markup too quickly. It has it's place, and with frameworks like Angularjs, is the centerpiece.

There needs to be an understanding that the whole <div id="myDiv" onClick="divFunction()">Some Content</div> was shamed so heavily because it was abused by some developers. So it reached the point of sacrilegious proportions, much like tables. Some developers actually avoid tables for tabular data. It's the perfect example of people acting without understanding.

Although I like the idea of keeping my behaviour seperate from my views. I see no issue with the markup declaring what it does (not how it does it, that's behaviour). It might be in the form of an actual onClick attribute, or a custom attribute, much like bootstraps javascript components.

This way, by glancing just at the markup, you can see what is does, instead of trying to reverse lookup javascript event binders.

So, as a third alternative to the above, using data attributes to declarativly announce the behaviour within the markup. Behaviour is kept out of the view, but at a glance you can see what is happening.

Bootstrap example:

<button type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">Click to toggle popover</button>

Source: http://getbootstrap.com/javascript/#popovers

Note The main disadvantage with the second example is the pollution of global namespace. This can be circumvented by either using the third alternative above, or frameworks like Angular and their ng-click attributes with automatically scope.

Chris
  • 43,908
  • 26
  • 122
  • 165
5
<whatever onclick="doStuff();" onmouseover="in()" onmouseout="out()" />

onclick, onmouseover, onmouseout, etc. events are actually bad for performance (in Internet Explorer mainly, go figure). If you code using Visual Studio, when you run a page with these, every single one of these will create a separate SCRIPT block taking up memory, and thus slowing down performance.

Not to mention you should have a separation of concerns: JavaScript and layouts should be separated!

It is always better to create evenHandlers for any of these events, one event can capture hundreds/thousands of items, instead of creating thousands of separate script blocks for each one!

(Also, everything everyone else is saying.)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Mark Pieszak - Trilon.io
  • 44,537
  • 13
  • 74
  • 89
  • What about angular, vue,... - they break concept of separation - and it not induce any problems in applications writen using this frameworks (in fact it speed up proces of creating software) - ? – Kamil Kiełczewski Jun 25 '20 at 15:12
  • 1
    @KamilKiełczewski Definitely since 2012 a lot has changed! This used to be a major SOC issue back then but now a days we do it everywhere. The only difference I suppose is that we're letting some framework compile our code and take (click) or whatever events and turn them into something different. But it's the same idea in the end right :D Crazy how things come back full circle! – Mark Pieszak - Trilon.io Jun 26 '20 at 00:49
  • I agree with you and I wrote down my thoughts [here](https://stackoverflow.com/a/54769527/860099) – Kamil Kiełczewski Jun 26 '20 at 06:23
4

Neither one is better in that they may be used for different purposes. onClick (should actually be onclick) performs very slightly better, but I highly doubt you will notice a difference there.

It is worth noting that they do different things: .click can be bound to any jQuery collection whereas onclick has to be used inline on the elements you want it to be bound to. You can also bind only one event to using onclick, whereas .click lets you continue to bind events.

In my opinion, I would be consistent about it and just use .click everywhere and keep all of my JavaScript code together and separated from the HTML.

Don't use onclick. There isn't any reason to use it unless you know what you're doing, and you probably don't.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Explosion Pills
  • 176,581
  • 46
  • 285
  • 363
  • 2
    Even if "better" isn't explicitly defined, it's almost never a good idea to put event handlers into your markup directly – Jay Sep 27 '12 at 19:31
  • @Jayraj when did I ever say in my answer that was a good idea? – Explosion Pills Sep 27 '12 at 20:28
  • 3
    By saying `.click` wasn't better than `onclick` ("Neither one is _better_") it was implied that `onclick` is almost, or just as good a way to write code, when in fact `.click` is the better practice by a mile. Sorry, but IMHO you should rephrase your answer – Jay Sep 27 '12 at 20:58
  • 2
    @ExplosionPills I would remove the part of the answer that says neither is better. The paragraph at the end sounds like a contradiction. – Juan Mendes Mar 11 '13 at 22:00
3

Most of the time, native JavaScript methods are a better choice over jQuery when performance is the only criteria, but jQuery makes use of JavaScript and makes the development easy. You can use jQuery as it does not degrade performance too much. In your specific case, the difference of performance is ignorable.

Koen Peters
  • 12,448
  • 6
  • 34
  • 57
Adil
  • 139,325
  • 23
  • 196
  • 197
3

Well, one of the main ideas behind jQuery is to separate JavaScript from the nasty HTML code. The first method is the way to go.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
supernova
  • 3,606
  • 2
  • 19
  • 34
2

The first method is to prefer. It uses the advanced event registration model[s], which means you can attach multiple handlers to the same element. You can easily access the event object, and the handler can live in any function's scope. Also, it is dynamic, i.e it can be invoked at any time and is especially well-suited for dynamically generated elements. Whether you use jQuery, an other library or the native methods directly does not really matter.

The second method, using inline attributes, needs a lot of global functions (which leads to namespace pollution) and mixes the content/structure (HTML) with the behavior (JavaScript). Do not use that.

Your question about performance or standards can't be easily answered. The two methods are just completely different, and do different things. The first one is mightier, while the second one is despised (considered bad style).

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
2

The first method of using onclick is not jQuery but simply Javascript, so you do not get the overhead of jQuery. The jQuery way can expanded via selectors if you needed to add it to other elements without adding the event handler to each element, but as you have it now it is just a question if you need to use jQuery or not.

Personally since you are using jQuery I would stick with it as it is consistent and does decouple the markup from the script.

Dustin Laine
  • 36,015
  • 9
  • 81
  • 121
  • 1
    The jQuery example is practically the same thing as `document.querySelector('#something').addEventListener(...` or similar in plain Javascript, so that isn't the point. Similarly you can catch bubbling events from child nodes in Javascript, not just with jQuery shortcuts. The heart of the matter is these events should be in the **controller** (javascript behavior definitions file) rather than the **view** (scattered here and there in the html, requiring global-variable-functions or worse yet, inline code.) – NoBugs Aug 02 '14 at 00:54
2

Onclick Function Jquery

$('#selector').click(function(){ //Your Functionality });

Love Kumar
  • 722
  • 7
  • 7
1

Performance

There are already many good answers here however, authors sometimes mention about performance but actually nobody investigate it yet - so I will focus on this aspect here. Today I perform test on Chrome 83.0, Safari 13.1 and Firefox 77.0 for solutions mention in question and additionally few alternative solutions (some of them was mention in other answers).

Results

I compare here solutions A-H because they operate on elements id. I also show results for solutions which use class (I,J,K) as reference.

  • solution based on html-inline handler binding (B) is fast and fastest for Chrome and fastest for small number of elements
  • solutions based on getElementById (C,D) are fast, and for big number of elements fastest on Safari and Firefox
  • referenced solutions I,J based are fastest for big num of elements so It is worth to consider use class instead id approach in this case
  • solution based on jQuery.click (A) is slowest

enter image description here

Details

Actually It was not easy to design performance test for this question. I notice that for all tested solutions, performance of triggering events for 10K div-s was fast and manually I was not able to detect any differences between them (you can run below snippet to check it yourself). So I focus on measure execution time of generate html and bind event handlers for two cases

  • 10 divs - you can run test HERE
  • 1000 divs - you can run test HERE

// https://stackoverflow.com/questions/12627443/jquery-click-vs-onclick
let a= [...Array(10000)];

function clean() { test.innerHTML = ''; console.clear() }

function divFunction(el) {
  console.log(`clicked on: ${el.id}`);
}

function initA() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> $(`#myDiv${i}`).click(e=> divFunction(e.target)));
}

function initB() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box" onclick="divFunction(this)">${i}</div>`).join``;
}

function initC() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> document.getElementById(`myDiv${i}`).onclick = e=> divFunction(e.target) );
}

function initD() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> document.getElementById(`myDiv${i}`).addEventListener('click', e=> divFunction(e.target) ));
}

function initE() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> document.querySelector(`#myDiv${i}`).onclick = e=> divFunction(e.target) );
}

function initF() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> document.querySelector(`#myDiv${i}`).addEventListener('click', e=> divFunction(e.target) ));
}

function initG() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> window[`myDiv${i}`].onclick = e=> divFunction(e.target) );
}

function initH() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  a.map((x,i)=> window[`myDiv${i}`].addEventListener('click',e=> divFunction(e.target)));
}

function initI() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  [...document.querySelectorAll(`.box`)].map(el => el.onclick = e=> divFunction(e.target));
}

function initJ() {
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  [...document.querySelectorAll(`.box`)].map(el => el.addEventListener('click', e=> divFunction(e.target)));
}

function initK() {  
  test.innerHTML = a.map((x,i)=> `<div id="myDiv${i}" class="box">${i}</div>`).join``;
  $(`.box`).click(e=> divFunction(e.target));
}



function measure(f) {  
  console.time("measure "+f.name);
  f();
  console.timeEnd("measure "+f.name)
}
#test {
  display: flex;
  flex-wrap: wrap;
}

.box {
  margin: 1px;
  height: 10px;
  background: red;
  font-size: 10px;
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>This snippet only presents used solutions. Click to solution button and then click on any red box to trigger its handler</div>
<button onclick="measure(initA)">A</button>
<button onclick="measure(initB)">B</button>
<button onclick="measure(initC)">C</button>
<button onclick="measure(initD)">D</button>
<button onclick="measure(initE)">E</button>
<button onclick="measure(initF)">F</button>
<button onclick="measure(initG)">G</button>
<button onclick="measure(initH)">H</button>
<button onclick="measure(initI)">I</button>
<button onclick="measure(initJ)">J</button>
<button onclick="measure(initK)">K</button>
<button onclick="clean()">Clean</button>

<div id="test"></div>

Here is example test for Chrome

enter image description here

Kamil Kiełczewski
  • 53,729
  • 20
  • 259
  • 241