valendesigns/option-tree — pull request #380 by designst (2014-08-07 17:52:15+00:00)
Title: Increase condition performance

Here i tried to fix some loss of performance in combination with the conditions feature. There is already an open issue #343 where someone has mentioned that the cause of the performance loss is in the registered keyup event.

But in my opinion there are several and different causes which i try to explain here.

In my option-tree configuration i use the list-item and quite a lot of sliders with attached conditions. After debugging the ```init_conditions()``` and ```parse_condition()``` functions i saw, that for every new list-item each slider of the whole settings page is initialized again and again.

```javascript
init_numeric_slider: function() {
$(".ot-numeric-slider-wrap").each(function() {
var hidden = $(".ot-numeric-slider-hidden-input", this),
value = hidden.val(),
helper = $(".ot-numeric-slider-helper-input", this);
if ( ! value ) {
value = hidden.data("min");
helper.val(value)
}
$(".ot-numeric-slider", this).slider({
min: hidden.data("min"),
max: hidden.data("max"),
step: hidden.data("step"),
value: value,
slide: function(event, ui) {
hidden.add(helper).val(ui.value);
},
change: function() {
OT_UI.init_conditions();
}
});
});
}
```

And after every slider initialization the conditions would be initialized again.

```javascript
change: function() {
OT_UI.init_conditions();
}
```

So i replaced every ```init_conditions()``` with ```parse_condition()``` except the one inside the main ```init()``` function.

In addition i added an optional **scope** parameter for some widget initialization functions to initialize only widgets inside a certain element. That gives me the opportunity to handle only the sliders inside the new list-item.

```javascript
init_numeric_slider: function(scope) {
scope = scope || document;
$(".ot-numeric-slider-wrap", scope).each(function() {

init_sortable: function(scope) {
scope = scope || document;
$('.option-tree-sortable', scope).each( function() {
```

```javascript
OT_UI.init_sortable(listItem);
OT_UI.init_select_wrapper(listItem);
OT_UI.init_numeric_slider(listItem);
```

There were still duplicate registrations on the select fields, because each ```init_select_wrapper()``` call bound an event listener for every select field again even those that already exists. I solved this problem by separating the initialization and the event registration.
```javascript
init_select_wrapper: function(scope) {
scope = scope || document;
$('.option-tree-ui-select', scope).each(function () {
```
```javascript
bind_select_wrapper: function() {
$(document).on('change', '.option-tree-ui-select', function () {
$(this).prev('span').replaceWith('' + $(this).find('option:selected').text() + '');
});
$(document).on($.browser.msie ? 'click' : 'change', '.option-tree-ui-select', function(event) {
$(this).prev('span').replaceWith('' + $(this).find('option:selected').text() + '');
});
},
```

Of course the **keyup** event is also a problem. For this wrote a simple ```function delay()``` witch will handle the keyup event only every 500ms.

```javascript
var delay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();

$('.format-settings[id^="setting_"]').on( 'change.conditionals, keyup.conditionals', OT_UI.condition_objects(), function(e) {
if (e.type === 'keyup') {
// handle keyup event only once every 500ms
delay(function() {
OT_UI.parse_condition();
}, 500);
} else {
OT_UI.parse_condition();
}
});
OT_UI.parse_condition();
```

After these changes the ```parse_condition()``` function is called a multiple less and the sliders doesn't listen multiple times to the same event.

There are even more possibilities to improve the performance and this changes are only a beginning, so If you have any comments or suggestions please feel free to let us know.