Reputation: 947
I have asked some way similar question yesterday, but after one day of research I've found it too complicated and not precise enough, so I deleted it.
So I have created another fiddle here, which should be self-descriptive in most parts.
Below, you can find a HTML and JavaScript part of it:
<select name="country">
<option value="spain">Spain</option>
<option value="sweden">Sweden</option>
<option value="russia">Russia</option>
<option value="canada">Canada</option>
</select>
<ul class="another-selector">
<li data-value="spain">Spain</li>
<li data-value="sweden">Sweden</li>
<li data-value="russia">Russia</li>
<li data-value="canada">Canda</li>
</ul>
And the promised JavaScript goes here:
// Following function is called when 'country_changed' event is triggered
var country_changed = function(event, data) {
$another_selector_items = $('.another-selector')
.children();
$another_selector_items
.removeClass('selected');
$another_selector_items.each(function(index, element) {
var $element = $(element);
if ($element.data('value') == data.country)
$element.addClass('selected');
});
var $select = $('select[name=country]');
$select
.children()
.removeAttr('selected')
.prop('selected', false)
.filter('[value=' + data.country + ']')
.attr('selected', 'selected')
.prop('selected', true);
// ----------------------------------------
// Uncomment this to get infinite recursion:
// ----------------------------------------
//$select.trigger('change');
};
// Register event bound to the document
// (so not attached to any particular element)
$(document)
.on("country_changed", country_changed);
// Bind changing the
$('select[name=country]').on('change', function(event) {
$.event.trigger('country_changed', {
country: $(this).val()
});
});
// Bind clicking on the LI to triggering the event
$('.another-selector').on('click', 'li', function(event) {
$.event.trigger('country_changed', {
country: $(this).data('value')
});
});
// Separate 'change' event binding --- required for 'pretty select' plugin
$('select[name=country]').on('change', function(event, data) {
// ...
});
The thing is, that I do not know what is the best approach to situations like this, when elements are both: listeners to the events AND have ability to TRIGGER the same events.
So, for example selecting an option from a native SELECT
element causes event to trigger and select appropriate LI
element on the other list. But as the event has to deal also with vice-versa situation, it also tries to update the SELECT
again, which is useless and - what's even worse - causes an infinite recursion (hopefully stopped by smart browsers).
Try to uncomment #46 line of JavaScript code in my fiddle to see what I am talking about.
So what I'm looking for is a nice way of detecting where the event came from, and then excluding it's source from the list of possible callbacks.
OR - maybe I'm trying to solve the problem in using wrong approach? Maybe I have read not enough for two days I've already spend on this? ;)
Please help me if you can.
Upvotes: 0
Views: 144
Reputation: 9888
I would separate UI generated events from the ones triggered from code. Also, there is no need for custom events and 2 change
handlers on SELECT
. Also, there is no need to re-trigger change
event from your custom event.
My suggestion for your problem is something like:
$('select[name=country]').on('change', function(event) {
var country = ... /* obtain country */
selectContry(country);
});
$('.another-selector').on('click', 'li', function(event) {
var country = ... /* obtain country */
selectContry(country);
});
function selectContry(country) {
// update view, e.g. SELECT and UL
// ...
// process new selection
// ...
}
In short: keep your UI event handlers short (identify context, i.e. data related to the event). The handler should then make a call to utility method (if appropriate via custom event) updating the view (if necessary) and processing the data.
If further events are required, do not (re)trigger the same ones as this is now new situation, the program state changed and different event (e.g. after_x
) is more appropriate.
If pretty select plugin bothers you, as part of updating the view do either:
update
method of prettyselect plugin: $('select[name=country]').prettyselect('update')
change.ps
event on select
: $('select[name=country]').trigger('change.ps')
Take a look at the source and see how it all works.
Upvotes: 1