Jquery tabs - Showing a confirmation before activate - jquery-ui

I'm using jquery UI's tabs. When a user switches tabs, I'm checking for unsaved changes, and prompting the user accordingly. However, before the user even clicks Yes or No, the tab loads anyway. Does anyone know how I can get my function to NOT return anything until after the result has come back from my fancybox dialog?
You can see I've tried a couple of different methods to return my boolean value in CheckSomething().
$("#myTabs").tabs({
select:
function (event, ui) {
return CheckSomething();
},
show:
function (event, ui) {
//do some stuff
}
});
function CheckSomething() {
var loadMyTab = true; //If I set this to false, then it always returns false.
if (myCondition) {
//Show a FancyBox prompt.
if (fancyYes) {
//return true;
loadMyTab= true;
}
else {
//return false;
loadMyTab = false;
}
}
else {
//return true;
loadMyTab = true;
}
return loadMyTab;
}

With a standard confirm instead of a fancy is working can it be a fast solution? http://jsfiddle.net/nuywj/

I ended up going w/ a standard javascript confirm box instead of FancyBox. That solved my problem, because Confirm will block code from being executed:
function CheckSomething() {
if (myCondition) {
return confirm("Are you sure you want to change tabs without saving?");
}
else {
return true;
}
}

Related

Lightbox2 Swipe gesture for touchscreens

To my great surprise Lightbox2(http://lokeshdhakar.com/projects/lightbox2/) does not support swipe gestures out of the box...
I was not able to find any add on in order to support this behavior. Anyone has any suggestions a side from changing the entire plugin? :)
To summary, you must hide the navigation buttons and implement swiping, moving and sliding effect on the image.
You will need :
jquery.event.move
jquery.event.swipe
jquery ui slide effect, you can package it in the jquery ui download builder
maybe there's a simplest way to get/implement all of these 3 small dependencies... but that way works for me.
in the lightbox script, add a new LightboxOptions enableSwipeOnTouchDevices and set it to true :
this.enableSwipeOnTouchDevices = true;
add the following blocks after the this.$lightbox.find('.lb-next').on('click'... block to create the swiping events:
this.$lightbox.find('.lb-image').on("swiperight",function() {
$('.lb-image').effect("slide", { "direction" : "right", "mode" : "hide"} ,function(){
if (self.currentImageIndex === 0) {
self.changeImage(self.album.length - 1);
} else {
self.changeImage(self.currentImageIndex - 1);
}
})
});
this.$lightbox.find('.lb-image').on("swipeleft",function() {
$('.lb-image').effect("slide", { "direction" : "left", "mode" : "hide"} ,function(){
if (self.currentImageIndex === self.album.length - 1) {
self.changeImage(0);
} else {
self.changeImage(self.currentImageIndex + 1);
}
})
});
and rewrite the updateNav function like this to hide the navigation buttons:
Lightbox.prototype.updateNav = function() {
// Check to see if the browser supports touch events. If so, we take the conservative approach
// and assume that mouse hover events are not supported and always show prev/next navigation
// arrows in image sets.
var alwaysShowNav = false;
var enableSwipe = false;
try {
document.createEvent("TouchEvent");
alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices)? true: false;
enableSwipe = (this.options.enableSwipeOnTouchDevices)? true: false;
} catch (e) {}
//if swiping is enable, hide the two navigation buttons
if (! enableSwipe) {
this.$lightbox.find('.lb-nav').show();
if (this.album.length > 1) {
if (this.options.wrapAround) {
if (alwaysShowNav) {
this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1');
}
this.$lightbox.find('.lb-prev, .lb-next').show();
} else {
if (this.currentImageIndex > 0) {
this.$lightbox.find('.lb-prev').show();
if (alwaysShowNav) {
this.$lightbox.find('.lb-prev').css('opacity', '1');
}
}
if (this.currentImageIndex < this.album.length - 1) {
this.$lightbox.find('.lb-next').show();
if (alwaysShowNav) {
this.$lightbox.find('.lb-next').css('opacity', '1');
}
}
}
}
}
};
I've used jquery mobile to detect swipeleft and swiperight. Then bind them to click .lb-next and .lb-prev. It's working now.
Here is my codepen.
PEC's solution worked for me with one modification on a Jekyll site.
Instead of:
this.enableSwipeOnTouchDevices = true;
We added this to /_includes/scripts.html after the dependencies and lightbox.js:
<script>
lightbox.option({
'enableSwipeOnTouchDevices': true,
})
</script>
The PEC solution is good, but it doesn't work anymore with the current version of lightbox (2.11.2). The effect() method doesn't exists anymore.
So the swiping methods should be updated:
this.$lightbox.find('.lb-image').on("swiperight",function() {
if (self.currentImageIndex === 0) {
self.changeImage(self.album.length - 1);
} else {
self.changeImage(self.currentImageIndex - 1);
}
return false;
});
this.$lightbox.find('.lb-image').on("swipeleft",function() {
if (self.currentImageIndex === self.album.length - 1) {
self.changeImage(0);
} else {
self.changeImage(self.currentImageIndex + 1);
}
return false;
});
Less fancy, but shorter and works.
In short: 'catch' swipe gesture and then trigger 'click' on next/prev button based on swipe direction.
let touchstartX = 0;
let touchendX = 0;
function handleGesture() {
if (touchendX < touchstartX) $(".lb-prev").trigger("click");
if (touchendX > touchstartX) $(".lb-next").trigger("click");
}
$(document).on("touchstart", ".lb-nav", e => {
touchstartX = e.changedTouches[0].screenX;
});
$(document).on("touchend", ".lb-nav", e => {
touchendX = e.changedTouches[0].screenX;
handleGesture();
});

How to check which subclass is the event fired?

I know event.type in DOM. I can parse for example mouseup, keydown, touchstart and so on. But how can I check for the event subclass? Like MouseEvent, AnimationEvent or ClipboardEvent? Can I use the event.type property?
You can check the class like
void myHandler(Event e) {
if(e is MouseEvent) {
print('Mouse');
} else if(e is AnimationEvent) {
print('Animation');
} else if(e is KeyboardEvent) {
print('Keyboard');
}
}
Since JavaScript is a prototype-based language you can to do it a bit strangely using Object.prototype.toString.call() and then cleaning up the result a little, like this:
var button = document.getElementById("testEvent");
button.onclick = function(e) {
console.log(
Object.prototype.toString.call(e).replace(/^\[object ([^\]]*)\]/, "$1")
);
}
This fiddle shows it in action - http://jsfiddle.net/SrmGJ/1/ working for me in FireFox. It should output "MouseEvent" in the fiddle, but if you hook it up to some of the other events, you will see different results.
Another method would be to call EventType.prototype.isPrototypeOf(e) for each of the types:
...
if (MouseEvent.prototype.isPrototypeOf(e)) { console.log("MouseEvent"); }
if (AnimationEvent.prototype.isPrototypeOf(e)) { console.log("AnimationEvent"); }
if (KeyboardEvent.prototype.isPrototypeOf(e)) { console.log("KeyboardEvent"); }
...
But that would look pretty nasty IMHO.

Angularjs: jquery selectable

i have created a directive to handle selectable provided by Jquery
mydirectives.directive('uiSelectable', function ($parse) {
return {
link: function (scope, element, attrs, ctrl) {
element.selectable({
stop: function (evt, ui) {
var collection = scope.$eval(attrs.docArray)
var selected = element.find('div.parent.ui-selected').map(function () {
var idx = $(this).index();
return { document: collection[idx] }
}).get();
scope.selectedItems = selected;
scope.$apply()
}
});
}
}
});
to use in html
<div class="margin-top-20px" ui-selectable doc-array="documents">
where documents is an array that get returned by server in ajax response.
its working fine i can select multiple items or single item
Issue: i want to clear selection on close button
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview
i can write jquery in controller to remove .ui-selected class but its not recommended approach
can some one guide me whats the best practice to achieve these type of issue
Update:
i fixed the issue by broadcasting event on cancel and listening it on directive
$scope.clearSelection=function() {
$scope.selectedItems = [];
$timeout(function () {
$rootScope.$broadcast('clearselection', '');
}, 100);
}
and in directive
scope.$on('clearselection', function (event, document) {
element.find('.ui-selected').removeClass('ui-selected')
});
is this the right way of doing it or what is the best practice to solve the issue.
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview

Datepicker using Jquery loses focus to the textbox after date selected.

Datepicker using Jquery loses focus to the textbox after date selected. I am using jquery-ui-1.9.2.When a date is selected the focus not coming to the textbox.Any solution?
Try using the below code.
HTML code:
<input type="text" id="date"/>
JQuery:
$("#date").datepicker({
onClose: function () {
$(this).focus();
}
});
JSFiddle1
EDIT: The above code has a problem in IE, the datepicker is not getting closed. Here in this blog you can find the more information.
<script language='javascript' src="jquery-migrate-1.2.1.js"></script> // download and add this
$("#date").datepicker({
/* fix buggy IE focus functionality */
fixFocusIE: false,
onClose: function(dateText, inst) {
this.fixFocusIE = true;
this.focus();
},
beforeShow: function(input, inst) {
var result = $.browser.msie ? !this.fixFocusIE : true;
this.fixFocusIE = false;
return result;
}
});
JSFiddle2
$(".datepicker").datepicker({
onClose: function () {
$(this).parents().nextAll().find($(":input[type !='hidden']")).first().focus();
}
});
});
I have found an easier way that will put the focus on the next input, no matter how nested it is. You can always swap out the condition after the .find to whatever you like and it will bring the focus to that.
Initialise all the datepcikers on Doc Ready
$('.datepicker').datepicker(
{
onClose: function () {
this.focus();
}
});
Exapnding Praveen's answer.
I had one problem with it. On IE datepicker refused to show up each odd time I focused a field.
Also, there was a slight logical issue with that solution (which did not affect anything, but still not correct to my eye): fixFocusIE field is being set on options, but then later it is being called on "this", when "this" refers to DOM element and not options object. So essentially there are two fixFocusIE - one in options (unused) and the second one on DOM element itself.
And also $.browser.msie did not work anymore, I had to invent my own IE detector.
My working code looks like that:
var el = $("selector of element I need to assign to datepicker");
var options = {}; // actually I fill options from defaults and do some other manipulations, but I left it as empty object here for brevity
options.fixFocusIE = false;
function detectIE() {
var ua = window.navigator.userAgent;
if(ua.indexOf('MSIE ') > 0 ||
ua.indexOf('Trident/') > 0 ||
ua.indexOf('Edge/') > 0) {
return true;
}
// other browser
return false;
}
/* blur() and change() are needed to update Angular bindings, if any */
options.onSelect = function(dateText, inst) {
options.fixFocusIE = true;
$(this).blur().change().focus();
};
options.onClose = function(dateText, inst) {
options.fixFocusIE = true;
this.focus();
};
options.beforeShow = function(input, inst) {
var result = detectIE() ? !options.fixFocusIE : true;
options.fixFocusIE = false;
return result;
};
/* and this one will reset fixFocusIE to prevent datepicker from refusing to show when focusing the field for second time in IE */
el.blur(function(){
options.fixFocusIE = false;
});
el.datepicker(options);

jQuery Mobile - Loading External HTML

I'm loading some external html into a div in a jquery mobile app. Everything works fine, however I'm trying to make it a little bit smoother.
Here is my code:
$(document).bind('pagebeforecreate', function (event, ui) {
if (event.target.id == 'pageViewOrder') {
//get the page
$.getJSON(root_url + '/orders/view/' + window.viewOrderReference + '/?callback=?', null, function (d) {
$("#viewOrder_content").html(d.html).trigger("create");
$.mobile.loading('hide');
});
}
What's happening is the page is being displayed prior to the ajax call finishing. Is there a way of halting jquery mobile from proceeding to display the page before this call is finished? At the moment it shows the page then the content pops in.
EDIT: This is loading in single pages
Cheers,
Ben
Halting the display process is easy, you just need to call event.preventDefault().
The problem is then to make sure that you will go with the process once you retrieve your content. What I would actually do is bind to pagechange, check if you have already retrieved the data, if not, then interrupt the process, retrieve the data and start over. If yes, then proceed as planned.
var contentRetrieved = false; //will indicate wether the JSON call has already been executed
var contentToDisplay; //data from the JSON call
$(document).live('pagebeforechange', function (event, data) {
if (( typeof data.toPage === "string" ) && ($.mobile.path.parseUrl(data.toPage).hash == '#pageViewOrder')) {
if (contentRetrieved) {
contentRetrieved = false; //content is already retrieved, we proceed with the pagechange
} else {
event.preventDefault(); //prevent further page change operations
$.getJSON(root_url + '/orders/view/' + window.viewOrderReference + '/?callback=?', null, function (d) {
contentToDisplay = {"html":d.html};
contentRetrieved = true;
$.mobile.changePage("#pageViewOrder");
});
}
}
});
$(document).bind('pagebeforecreate', function (event, ui) {
if (event.target.id == 'pageViewOrder') {
$("#viewOrder_content").html(contentToDisplay.html).trigger("create");
$.mobile.loading('hide');
}
});​

Resources