jquery $.on() and touch event handling odd behavior when using date() - events

In order to implement a "long press" on mobile, I started with a simple event map where touchstart sets a time, touchend sets another and then calculates the difference to see how long the element was pressed. This was my old code:
$('html')
.on({
touchstart : function(e){
g.tt.start = new Date();
},
touchend : function(e){
g.tt.end = new Date();
g.tt.delta = g.tt.end - g.tt.start;
alert(g.tt.delta);
g.tt = {};
}
})
;
but unfortunately...
...every other press kept calculating the difference from the previous touchend to the touchstart. I'm pretty sure there is something basic I'm missing and thus end up over-engineering this (no formal training I'm afraid). Here is my new code:
$('html')
.on({
touchstart : function(e){
g.tt = {};
g.tt.start = new Date();
},
touchend : function(e){
g.tt.end = new Date();
g.tt.delta = g.tt.end - g.tt.start;
if( isNaN(g.tt.delta) == false ) {
alert(g.tt.delta);
}
else {
return false;
}
setTimeout(function(){g.tt = {}; });
}
})
;
Shouldn't there be a much easier way to do this with less clauses? Never mind my funny g.tt variable names.

I'm realizing that I took the wrong approach, and also found a nice plugin:
http://aanandprasad.com/articles/jquery-tappable/

You are timing the difference between two events, so there is no need to use setTimeout, just use closure to share some variables between the two events. This example is taken from this answer:
(function () {
var start, end;
$('html').on({
touchstart : function(e){
start = +new Date(); // get unix-timestamp in milliseconds
},
touchend : function(e){
end = +new Date();
g.tt.delta = end - start;
if( isNaN(g.tt.delta) == false ) {
alert(g.tt.delta);
}
}
});
})();
Here is a demo showing this with mouse events and touch events

Related

Sencha EXT JS tree check/uncheck very slow while filtered

Recursive check/uncheck for tree with 116 nodes takes 9-13ms with function:
checkchange: function (record, checked, opts) {
var i = 0;
var start = new Date();
this.suspendLayouts();
record.cascadeBy(function (e) {
i++;
e.set('checked', checked);
});
this.resumeLayouts();
var stop = new Date();
alert(i + 'items ' + (stop - start) + 'ms');
}
But if store is filtered by one of the fields this becomes very slow (2000ms).
This is the filter:
load: function () {
this.getStore().filter({
property: 'account_id',
value: 3934,
operator: '='
});
}
Seems that suspendLayouts not working and every time on e.set('checked', checked); filter processed on all tree and rerender all nodes.
You can check on https://fiddle.sencha.com/#view/editor&fiddle/3l62
just comment/uncomment filter code to see difference.
Any way to make it faster?
Found workaround solution that suppress store events during batch update but refresh needed for view after update finished. Before update you must call this.getStore().suspendEvents();
and after:
this.getStore().resumeEvents();
and refresh, depending on bufferedRenderer:
if (this.getView().bufferedRenderer) {
this.getView().bufferedRenderer.refreshView();
} else {
this.getView().refresh();
}
Complete code:
checkchange: function (record, checked, opts) {
var i = 0;
var start = new Date();
this.suspendLayouts();
this.getStore().suspendEvents();
record.cascadeBy(function (e) {
i++;
e.set('checked', checked);
});
this.resumeLayouts();
this.getStore().resumeEvents();
if (this.getView().bufferedRenderer) {
this.getView().bufferedRenderer.refreshView();
} else {
this.getView().refresh();
}
var stop = new Date();
alert(i + 'items ' + (stop - start) + 'ms');
}

failing to reset language selection after sync

I am facing a problem which I am not aware how to resolve. Let me describe elaborately below:
I have a commonViewModel kendo class where event like save, cancel are written. I am facing problem with the save event of this class.
save: function () {
var that = this;
var routeLanguage = "";
that._showBackConfirmation(false);
that.set("isFormSubmitted", true);
console.log("form is valid, sending the save request!");
if (vm.get("languageTabsVm.selectedLanguage")) {
routeLanguage = "/" + vm.get("languageTabsVm.selectedLanguage");
}
else if (that.get("model.Languages") && that.get("model.Languages").length > 1) {
that.get("model.Languages").forEach(function (lang) {
if (lang.get("IsActive") === true) {
//sätt cv-visning till det språk jag senast redigerade på detta item
routeLanguage = "/" + lang.LanguageId;
}
});
}
//if i call the function _loadDefaultLanguageSelection here, it
// works. because, the datasource is not synced yet.
//Make sure the datasource are syncing changes to the server (includes all crud)
return that.dataSource.sync().fail(function (e) {
//i need to do something here to be in the same language tab. But
//as i am changing directly in to model, it is not possible. But
//saving directly to model is essential because that model is
//shared to other viewmodel for language tab synching purpose.
that.set("isFormSubmitted", false);
console.log("form rejected");
}).done(function () {
if (that.get("isPersonaldetail")) {
var name = that.get("model.Name");
if (name.length > 12)
name = name.substring(0, 11) + "...";
$("#profileName").text(name);
}
that.set("isFormSubmitted", false);
that.set("isSelected", false);
// it is called from here right now. but it is failing because
// model is updated but not synced in that function
that._loadDefaultLanguageSelection();
router.navigate(that.nextRoute + routeLanguage);
});
},
_loadDefaultLanguageSelection: function () {
var that = this;
if (that.get("model.Languages") && that.get("model.Languages").length > 1) {
that.get("model.Languages").forEach(function (lang) {
if (!that.get("isPersonaldetail")) {
lang.set("IsActive", lang.get("LanguageId") === vm.get("languageTabsVm.selectedLanguage"));
}
});
}
},
So, my question is, how can i resolve this problem. one solution is i will have to sync twice. that is not nice. So, I am looking for efficient solution.

Slideshow using Prototype and Scriptaculous

I wrote my first scriptaculous script to create a slideshow between some div element :
var SlideShow = Class.create({
initialize:function(element, delayStart){
this.element = element;
this.delayStart = delayStart;
this.slides = this.element.childElements();
this.numberOfSlides = this.slides.size();
this.numberActiveSlide = 1;
this.start_slideshow();
},
start_slideshow: function()
{
this.switch_slides.delay(this.delayStart);
},
switch_slides: function()
{
this.slides[this.numberActiveSlide].fade();
if (this.numberActiveSlide == this.numberOfSlides) { this.numberActiveSlide = 1; } else { this.numberActiveSlide = this.numberActiveSlide + 1; }
Effect.Appear.delay(this.slides[this.numberActiveSlide], 850);
this.switch_slides.delay(this.delay + 850);
}
});
document.observe("dom:loaded", function(){
var slideshows = $$('div.slideshow');
slideshows.each(
function(slideshow) {
s = new SlideShow(slideshow, 2);
});
});
But I always get this error and It's been hours I can't figure it out where my problem is!
Undefined is not an object (evaluating this.slides[this.numberActiveSlide]);
Thanks you !
Nick
99% sure it's a context issue. Make sure you bind your function calls so that this is retained throughout your code.
Debug what this is in switch_slides: it should be the same thing as this in start_slideshow. If it's not, bind your call to switch_slides to your instance:
start_slideshow: function()
{
this.switch_slides.bind(this).delay(this.delayStart);
},
You'll probably have to do the same in switch_slides where it calls itself.

Conditionally pause Javascript to wait for ajax

The variable ajaxdata is modified within the success function, if that hasn't been done yet, I would like to wait 2 seconds, then continue without it.
The use case is for a jqueryui autocomplete field. The autocomplete source is an ajax request, but if the user types quickly, and exits the field before the list loads, the field remains unset. Using the 'change' event on the autocomplete I check if the user entered a valid option without selecting it, but this doesn't work if the source hasn't loaded when the change event fires. So I would like to put a delay in the change function which waits, if the source (stored in the variable 'ajaxdata') is empty.
code:
input.autocomplete({
source: function (request, response){
$.ajax(
{
type: "GET",
url: "/some/url",
dataType: "json",
success: function(data){
response($.map(data,function(item){
return{
label: item.label,
value: item.value
}
}));
ajaxdata = data;
}
}
);
// ajaxopts = ajaxsource(request,response,ajaxurl,xtraqry)
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = $(this).val();
var stringMatch = false;
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
}
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(this).val(opts[i].label);// corrects any incorrect case
stringMatch = true;
break;
}
}
}
},
});
Edit:
To be more specific about the problem: This delay needs to be conditional. Meaning that if the data is already loaded (either because it came from a static source, or from an earlier ajax call) I do not want to have a delay.
If I'm understanding you properly, I think you just want to check and see if ajaxdata has been populated; but if it hasn't, only wait two more seconds and then just proceed without it.
Try this:
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
//pass in 'this' so that you can use it
setTimeout(function() {correctCase(this);}, 2000);
}
}
}
. . . . .
function correctCase(inThis){
//I'm not sure what this variable does. do you really need it???
var stringMatch = false;
var enteredString = $(inThis).val();
//you still want to be sure that ajaxdata is not empty here
if (ajaxdata.length==0){
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(inThis).val(opts[i].label); // corrects any incorrect case
stringMatch = true; //this variable doesn't seem to do anything after this???
break;
}
}
}
}
I'm not really sure what it is you're trying to do, but I'm pretty sure something like this would be a better way of doing it :
input.autocomplete({
source: function(request, response) {
return $.ajax({
type: "GET",
url: "/some/url",
dataType: "json"
});
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = this.value;
var stringMatch = false;
//make sure ajax is complete
this.source().done(function(data) {
var opts = $.map(data, function(item) {
return {
label: item.label,
value: item.value
}
});
for (var i = 0; i < opts.length; i++) {
if (opts[i].label.toLowerCase() == enteredString.toLowerCase()) {
$(this).val(opts[i].label); // corrects any incorrect case
stringMatch = true;
}
}
});
}
}
});​
By default, JavaScript is asynchronous whenever it encounters an async function, it queued that function for later.
But if you want a pause js(ajax call or anything) for you can do it use promises
Case 1: output hello(will not wait for setTimeout)
https://jsfiddle.net/shashankgpt270/h0vr53qy/
//async
function myFunction() {
let result1='hello'
//promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
//});
//result = await promise
alert(result1);
}
myFunction();
case 2: output done1(will wait for setTimeout)
https://jsfiddle.net/shashankgpt270/1o79fudt/
async function myFunction() {
let result1='hello'
promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
});
result = await promise
alert(result1);
}
myFunction();

How to Fix Poor Performance on Filter By Text

A while back I found some code that allows you to filter the contents of a SELECT by typing in a text element. It works well however, over time the performance degrades pretty badly. I'm not sure if it is the filter code or the way in which I am activating it.
The SELECT shows up in a modal dialog (bootstrap) so I have the following code:
$('#myModal').on('shown', function () {
$(".focusable").val("").focus();
var select = $('#myModal').find(".modal-body").find("select");
var text = $('#myModal').find(".modal-body").find("input[type='text']");
select.filterByText(text, true);
});
And here is the filter code:
jQuery.fn.filterByText = function (textbox, selectSingleMatch) {
return this.each(function () {
var select = this;
var options = [];
$(select).find('option').each(function () {
options.push({value:$(this).val(), text:$(this).text(), data:$(this).data("short-name")});
});
$(select).data('options', options);
$(textbox).bind('change keyup', function () {
var options = $(select).empty().data('options');
var search = $.trim($(this).val());
var regex = new RegExp(search, 'gi');
$.each(options, function (i) {
var option = options[i];
if (option.text.match(regex) !== null) {
var copyOption = $('<option>').text(option.text).val(option.value);
copyOption.data("short-name", option.data);
$(select).append(copyOption);
}
});
if (selectSingleMatch === true &&
$(select).children().length === 1) {
$(select).children().get(0).selected = true;
}
});
});
};
Can anyone shed some light on where my performance issue(s) might be and how to solve it?
reading through the comments I would suggest to add the following:
$(textbox).bind('change keyup', function(event) {
console.log(event);
// your code
});
Is the event triggered more than once on a single keyup after some times the dialog is shown?
$('#myModal').on('hidden', function () {
$('#myModal').find(".modal-body").find("input[type='text']").off("change keyup");
});

Resources