Handling HTML SELECT Options with event delegation to javascript on iPhone Safari - javascript

We are developing a web application with GUI customized to use on iPhone. A page on the app has 3 subsequent SELECT dropdowns, where selection of an option from 1st dropdown derives the options for 2nd dropdown, and so forth. The subsequent dropdown options are populated by javascript based on onchange event on previous dropdown.
The problem is that, on iPhone the options for a SELECT appears with 'prev' and 'next' links to move to previous and next control. When the 'next' link is clicked, then the control moves to next SELECT and displays the options. The javascript is triggered by onchange event for previous SELECT and populates the options for next SELECT. But on dropdown for 2nd SELECT displays the previous options before it is repopulated by the javascript.
Is there a workaround or event that can sequence execution of javascript and then selection of next control? Is there any event that can be triggered when a SELECT option is selected and before control leaves the SELECT element? Is there a handle for Next and Previous links for SELECT dropdown option display?

Maybe you could use the focus event, in jQuery this would be:
$('#select2').focus(function () {
// update select2's elements
});
Although the real question is when the iPhone overload comes in, and when the event get fired. And also can the select options be changed whilst in this view.

I had the same problem on my site. I was able to fix it by manually polling the selectedIndex property on the select control. That way it fires as soon as you "check" the item in the list. Here's a jQuery plugin I wrote to do this:
$.fn.quickChange = function(handler) {
return this.each(function() {
var self = this;
self.qcindex = self.selectedIndex;
var interval;
function handleChange() {
if (self.selectedIndex != self.qcindex) {
self.qcindex = self.selectedIndex;
handler.apply(self);
}
}
$(self).focus(function() {
interval = setInterval(handleChange, 100);
}).blur(function() { window.clearInterval(interval); })
.change(handleChange); //also wire the change event in case the interval technique isn't supported (chrome on android)
});
};
You use it just like you would use the "change" event. For instance:
$("#mySelect1").quickChange(function() {
var currVal = $(this).val();
//populate mySelect2
});
Edit: Android does not focus the select when you tap it to select a new value, but it also does not have the same problem that iphone does. So fix it by also wiring the old change event.

Related

How to determine if an Element was created dynamically

I have an existing system built using jQuery, Backbone.js and a REST-ish back-end written in C#. The application is an SPA with forms to fill and navigation. My job is to build a "Navigation Interceptor" to connect on the application so the system detects whether a field was modified in the current view so that when the user navigates, he will be warned that fields were modified and be requested to save or cancel the changes.
The way I designed it is using jQuery. To make it short, I use a selector on input, select, etc.. and bind a change event to it. Then I use a selector on links and buttons, unbind all click events, bind my "interceptor" (if a field has changed, ask before navigating) and then rebind all click events after my interceptor. I use stopImmediatePropagation() to cancel the regular navigation events, resulting in a kind of wrapper around the events.
By doing so, I have 2 problems:
Calling .val() on a field does not trigger the change event which is fine since I populate the fields dynamically. The problem is that the bootstrap date pickers does not seem to be setting the value using .val() resulting in all date fields having the "changed" state when initialized.
Elements dynamically created (e.g.: field in accordion panel created after the page has loaded) don't accept the events resulting in forms not firing the change event of my navigation interceptor.
My question is regarding the 2 above elements:
Is there a way to determine if a specific field is a date picker and bind the change event on that field so that when I populate it, the change event does not fire, but when the users do, it does (I tried binding on the changeDate event but the setDate method seems to be firing the changeDate event also)?
Is there a way to determine if the element was dynamically created (e.g.: $(''))? The problem is that I do not have a specific selector for a single field, so I think I cannot use delegation ($(document).on('change', 'someFieldSelectorICannotKnow', function () {});). All I have is a handle on the jQuery element ($(this) in a .each(fn) iteration).
#2 Solved using event delegation on all fields and skipping the handler if the field is not a form field
Solution of #2:
NavigationInterceptor.prototype.bindChangeEventOnAllEditableFields = function () {
var self = this;
var fieldsSelector = $(this.configuration.selectors.formFields.join(', '));
$(fieldsSelector).each(function () {
var isFormField = !self.searchClassFromArrayInElement(self.configuration.classes.nonFormFieldClasses, $(this));
if (isFormField && self.configuration.debug.highlight.fields.unchanged && $(this).attr('type') === 'radio') {
$(this).parent().css('background-color', self.configuration.debug.highlight.fields.unchanged);
} else if (isFormField && self.configuration.debug.highlight.fields.unchanged) {
$(this).css('background-color', self.configuration.debug.highlight.fields.unchanged);
}
});
$(document).on('change', fieldsSelector, function (event) {
var field = $(event.target);
var isFormField = !self.searchClassFromArrayInElement(self.configuration.classes.nonFormFieldClasses, field);
if (isFormField) {
self.hasFieldChanged = true;
if (self.configuration.debug.highlight.fields.changed) {
if (field.attr('type') === 'radio') {
field.parent().css('background-color', self.configuration.debug.highlight.fields.changed);
} else {
field.css('background-color', self.configuration.debug.highlight.fields.changed);
}
}
}
});
return this;
}
var unchangeable_classes = ['decorative', 'constant'];
$(document).on('change', 'input,select,textarea', function () {
var $this=$(this);
for(var i =0;i<unchangeable_classes.length;++i){
if($this.hasClass(unchangeable_classes[i]))
return;
}
global_changed = true;
});
Why doesn't this work, it should? (Edited in response to comment.)
My thoughts>>
1)A way to stop calling changeDate() after calling setDate(), you could just call event.stopPropogation(), it will prevent the event from bubbling up
2)while creating a dynamic element, you could add any property of your wish. For eg
var iDiv = document.createElement('div');
iDiv.isDynamic = true;
then while iterating through the element, check for isDynamic property
Why not add a benign class tag to the element
$('#foo').addClass('bar');
then you can check for the class to see if it was created
if ($('#foo').hasClass('bar'))
alert('was created');
Note that when you add elements you have to re-attach the events. So if you have a global document event and then add an element, that element won't be included unless you explicitly attach the new element.
I'm posting the answer for my question #1.
What I did is modify bootstrap's source. When calling setDate like so:
$('#myDateInput').datepicker('setDate', new Date());
The code goes through the function setDates which calls update and setValue, the first one resulting in the date being set in the datepicker itself, the second one setting only the value in the input text field. What I did is remove the call to 'change' which triggers the change event on the field and left the custom event 'dateChange'. This results in my code not firing the change event when I call setDate, but calls it when the user sets a date using the picker itself.

Is it possible to remove focus from ZK combobox after list appears?

IE 10 has a bug
https://connect.microsoft.com/IE/feedback/details/841043/blinking-text-cursor-overlapping-with-div
But I need somehow remove blinking cursor in ZKoss Combobox. The primary idea is to remove focus from input and try to get it to popup list. I think that if focus will be removed, blinking curcor doesn't appears behind popup list.
How can I remove focus from combobox's input after the popup list appears? Is it possible?
My hint is to set the combobox readonly.
If it is not possible, you can try to highlight the value of combobox.
I use this to get the trick, in .zul:
<combobox onFocus="#command('setFocusOnRow', evt=event)" />
and this in viewmodal:
#Command
public void setFocusOnRow(#BindingParam("evt") Event event) {
Combobox c = (Combobox) event.getTarget();
// select: highlight the value
c.select();
}
However, the last time I used this it didn't work using Chrome.
Seems like I solve it!
The point is to override method open of ZKoss's Combobox.
var _Cwgt = {};
zk.override(zul.inp.Combobox.prototype, _Cwgt, {
open: function (silent) {
var id = '#' + this.uuid + '-pp'; // get id of popup
_Cwgt.open.apply(this, arguments); // call original
jq(id).focus(); // give focus to the popup
}
});

Get items from Select2 after data has been loaded

I'm trying to force the open method and select item with select2 like this.
$(".select").select2('open')
$(".select").on('select2-loaded', function (e) {
items = e.items.results;
if (items.length == 1) {
$(this).val(items[0].text);
}
});
$(".select").select2('close');
But I cannot get inside the select2-loaded evaluation and the close is happening also to quick. I want to select the first element by default if there is only one element on the list.
When the 'open' occurs, it automatically performs the mechanism to get and load all the elements in my list. If I comment the close method it will take some milliseconds to display the elements on the list.
But what I want is to refresh my list (this is done by the open) then I want to select only if there one element and the close my list ('select')
How can I achieve this? This behaviour will be trigger by a onchange event of another list. (nested selects)
You should move the close call INSIDE the select2-loaded event, so it'll only close the select element after it selected the item. Otherwise you're opening, setting event for future event, then closing immediately.
$(".select").select2('open')
$(".select").on('select2-loaded', function (e) {
items = e.items.results;
if (items.length == 1) {
$(this).val(items[0].text);
$(".select").select2('close');
}
});

Show div upon tabbing into a certain input field

I want to show a date picker when the user gets to a certain input on a form. The datepicker should display if the user simply tabs into the field.
I tried something like this (I'm using Zebra_DatePicker by the way):
var datepicker = $('#date-picker-input').data('Zebra_DatePicker')
$('#date-picker-input').focus(function(){
datepicker.show()
})
That works - tabbing into the field shows the datepicker. However, this breaks the functionality if the user decides to click on the input field in question; when this happens, the click initially opens the datepicker, and then the focus callback function closes it. The result is a split-second flicker where the datepicker is shown then closes instantly.
How can I get functionality for both clickers and tabbers?
The problem is that data('Zebra_DatePicker') returns a reference to the date-picker component and not to the element itself so your focus event binding is performed on the wrong object.
Try this:
$('#date-picker-input').on('focus click', function(e) {
e.stopImmediatePropagation();
$(this).data('Zebra_DatePicker').show();
})
The plugin does not support what you are asking nor does it offer many hooks, so the workaround will be a bit hacky..
If you are working with a single datepicker in the page do
on initialization of plugin
$('#date-picker-input').Zebra_DatePicker({
// what options you already use
// and then add
onSelect: function () {
var datepicker = $(this).data('Zebra_DatePicker');
setTimeout(function () {
datepicker.hide();
}, 1);
}
});
and then add
$('#date-picker-input').off('click').on('focus', function(){
var datepicker = $(this).data('Zebra_DatePicker');
datepicker.show();
return false;
});

Is there any way to enable day-related events in dojo's DateTextBox?

I'm using dojo's DateTextBox with a connected onChange event.
There are cases users select the same day as already selected, which unfortunately (but of course logically) doesn't fire an onChange event. But I still need to trigger the onChange-connected actions regardless of whether the user selected the same value again or did not.
Is there any way to connect events to single/all DateTextBox days bypassing the onChange event of the box? I think the days aren't widgets themselves. An even better option would be some kind of onSelect event for DateTextBox, which apparently doesn't exist...
You need to connect to onChange event on popup calendar, not DateTextBox. The problem is that every time you open calendar DateTextBox destroys previously shown instance and creates new instance of dijit.Calendar (referenced in DateTextBox.dropDown property).
So you need to connect to DateTextBox.openDropDown() method to create connect to DateTextBox.dropDown.onChange(), i.e. Calendar.onChange():
var dateBox = dijit.byId("dateBox");
var eventHandle;
dojo.connect(dateBox, "onChange", function(value) {
// fires only if clicked date changed
console.log('onChange');
});
dojo.connect(dateBox, "openDropDown", function() {
eventHandle = dojo.connect(dateBox.dropDown, "onChange", function(value) {
// fires every time date is clicked
console.log(value.toString());
});
});
dojo.connect(dateBox, "closeDropDown", function() {
dojo.disconnect(eventHandle);
});
See this example in action at jsFiddle.

Categories

Resources