Created
March 21, 2012 19:18
-
-
Save slickplaid/2151586 to your computer and use it in GitHub Desktop.
Focus next tab item on fieldset exit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).on('focus', '.message-form .note textarea, .message-form .subject input', function() { | |
var textarea = $(this); // or input subject | |
var tab = $('.content-links').find('.selected'); | |
// make sure none of the inputs are those in the note form | |
var formElements = $(this).parents('form').find('input'); | |
var inputElement = $(this).parents('form').find('input[type=submit]'); | |
var bindings = $('a, input').not(formElements); | |
bindings.bind('click.preventNavigation', function(e) { | |
if(textarea.val() !== '') { | |
if(e.isDefaultPrevented()) { | |
// if there's already an event handler on the element that is preventing the default action | |
noteConfirmation(textarea, tab, bindings, false); | |
} else { | |
// if there isn't an event handler, we want to allow them to continue with the action, so we pass it along | |
e.preventDefault(); | |
noteConfirmation(textarea, tab, bindings, $(this)); | |
} | |
} else { | |
// if note is empty, we don't want to do anything so remove any trace this was here | |
bindings.unbind('click.preventNavigation'); | |
} | |
}); | |
inputElement.bind('click.cancelNotePrompt', function(e) { | |
// reset everything so it won't be called again | |
inputElement.unbind('click.cancelNotePrompt'); | |
bindings.unbind('click.preventNavigation'); | |
}); | |
}); | |
function noteConfirmation(textarea, tab, bindings, clickedElement) { | |
// unbind the event so it won't register any more clicks until they reblur the textarea again | |
bindings.unbind('click.preventNavigation'); | |
var html = [ | |
'<fieldset class="inline-form note-confirmation">', | |
'<legend class="inline-form-head">Message Confirmation</legend>', | |
'<div class="content-alerts content-errors">', | |
'<h4 class="title">You have an message that was not saved or sent...</h4>', | |
'</div>', | |
'<p class="form-actions">', | |
'<a class="action note-back" href="#">Please take me back!</a>', | |
'<a class="cancel-link" href="#">cancel</a>', | |
'</p>', | |
'</fieldset>'].join(''); | |
if($('.note-confirmation').length === 0) { | |
// we want to make sure only one overlay gets created | |
$('body').append(html); | |
} | |
$('.note-confirmation').one('click', 'a', function(e) { | |
var $this = $(this); | |
e.preventDefault(); // we'll stop the links in the confirmation box from doing anything normally | |
// prompt gets removed/hidden regardless of what they select | |
$('.note-confirmation').hide().remove(); | |
if($this.hasClass('note-back')) { // we still have reference to the removed object | |
// return them to the previous tab (if applicable) and focus the textarea | |
tab.find('a').click(); | |
} else { | |
if(clickedElement) { // return the normal browsing path to whatever they were doing | |
if(clickedElement.is('a')) { | |
var url = clickedElement.attr('href'); | |
window.location.assign(url); | |
} else { | |
clickedElement.click(); // force the normal action | |
} | |
} | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).on('keydown.tab', '.field-group input, .field-group select, .field-group textarea, .field-group h4', function(e) { | |
if(e.which === 9) { // tab key | |
var $this=$(this); | |
var $fieldGroup = $this.parents('.field-group'); | |
var focusedElement = $this.get(0); | |
var id = $fieldGroup.attr('id'); | |
var lastElement = $fieldGroup.find('input, select, textarea, button, h4'); | |
lastElement = lastElement.not(':hidden'); | |
lastElement = lastElement.filter(function() { | |
return $(this).css('visibility') !== 'hidden'; | |
}); | |
lastElement = lastElement.last().get(0); | |
if(focusedElement === lastElement) { | |
var $fieldGroupIndex = $('.field-group-index'); | |
var $tab = $fieldGroupIndex.find('li').find('a[href$=#'+id+']'); | |
var $nextTab = $tab.parent('li').next().find('a'); | |
var lastFGElement = $fieldGroupIndex.find('li').last().find('a').get(0); | |
var tabElement = $tab.get(0); | |
if(lastFGElement !== tabElement && $tab.length) { | |
e.preventDefault(); | |
$nextTab.focus(); | |
} | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// As of 2011-11-22, GCF has an outstanding issue where tab index is not maintained properly. This causes the tab focus | |
// to switch to the first possible tabbable element the first time TAB is pressed, regardless of what other element was | |
// previously focused. Once the tab key is pressed the first time, tabbing occurs naturally. This behavior resets each | |
// time a page is navigated to in GCF. (If GCF registry entry "HandleTopLevelRequests" is set to "0", then the behavior | |
// does not reset on a simple navigation, but does reset with new windows, etc). | |
// This workaround adds a handler for focusout (which is triggered when the user tabs out of an element) capturing which | |
// element was last focused. It then checks if the focus is traversing TO the first possible tabbale element (a div with | |
// ID: ChromeFrameWorkaroundDiv, which is inserted into the DOM programatically) and if so, refocuses the previously | |
// focused element. It assumes any time the ChromeFrameWorkaroundDiv div is focussed, it was erroneous. It also assumes | |
// the ChromeFrameWorkaroundDiv div is the first tabbable element in the DOM. | |
// See http://code.google.com/p/chromium/issues/detail?id=102177 for the current status of the issue as tracked at the | |
// Chromium project. | |
// This workaround uses the jQuery framework. | |
function addChromeFrameFocusWorkaround() { | |
// Only apply workaround if we are in Google Chrome Frame (window.externalHost is the indicator) | |
if (navigator.userAgent.indexOf("Chrome") != -1 && window.externalHost) { | |
$(document).ready(function() { | |
// Add the empty div to the DOM | |
$("body").prepend("<div style='width: 0; height: 0;' tabindex='1' id='ChromeFrameWorkaroundDiv'></div>"); | |
// Track the element that is losing focus | |
var lastFocus = null, | |
fsElem = null, | |
index = null, | |
newFocus = null, | |
h4Parent = null; | |
$(document).focusout(function(evt) { | |
if (evt && evt.target) { | |
lastFocus = $(evt.target); | |
// we will get next target rather than move back to the original selection | |
fsElem = lastFocus.parents('body').find('a, input, textarea, select, h4').not('[tabindex="-1"]'); | |
index = fsElem.index(lastFocus); | |
if(lastFocus.is('h4')) { | |
h4Parent = lastFocus.parents('.autocomplete-results'); | |
fsElem = h4Parent.find('h4'); | |
index = fsElem.index(lastFocus); | |
} | |
newFocus = fsElem.eq(index+1); | |
} | |
}); | |
// When focus is given to the ChromeFrameWorkaroundDiv, give it back to the previous element. | |
$("#ChromeFrameWorkaroundDiv").focusin(function() { | |
if(newFocus && index > -1) { | |
newFocus.focus(); | |
} else { | |
lastFocus.focus(); // we'll refocus the last focused item in case something goes wrong. | |
// That way people won't lose their mind having to move their hand to the mouse to refocus and keep moving | |
} | |
if(h4Parent) setTimeout(function() { | |
h4Parent.show(); | |
}, 10); // probably could drop this, but at 10ms, it's almost unnoticable | |
// basically makes it guaranteed that it'll fire after the hide event is called `ie: nextTick()` | |
}) | |
}); | |
} | |
} | |
addChromeFrameFocusWorkaround(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// define keys | |
var keymap = { | |
'copy' : 119, // F8 | |
'edit' : 120, // F9 | |
'add' : 115, // F4 | |
'left' : 37, | |
'up' : 38, | |
'right': 39, | |
'down' : 40 | |
} | |
// map keys | |
keymap.allKeys = []; | |
for(var key in keymap) { | |
keymap.allKeys.push(keymap[key]); | |
} | |
var optNumber = false; // for arrow keys on edit button | |
// keydown.shortcuts is namespaced so we can disable them if needed by calling: | |
// $(document).off('keydown.shortcuts'); | |
$(document).on('keydown.shortcuts', function(e) { | |
// check to see if key used is a shortcut key | |
if(keymap.allKeys.indexOf(e.which) > -1) { | |
var action = false; | |
if(e.which === keymap.copy) { | |
action = $('.copy-action'); | |
followUrl(action); | |
handleDropdown(action); | |
} | |
if(e.which === keymap.edit) { | |
action = $('.edit-action'); | |
followUrl(action); | |
handleDropdown(action); | |
} | |
if(e.which === keymap.add) { | |
action = $('.add-action'); | |
followUrl(action); | |
handleDropdown(action); | |
} | |
} | |
function followUrl(action) { | |
// if action has href, follow it | |
// click() doesn't work on these for whatever reason | |
var url = action.eq(0).attr('href'); | |
if(url) window.location.assign(url); | |
} | |
function handleDropdown(action) { | |
if(!action.hasClass('action-group')) | |
action = action.parent('.action-group'); | |
action.toggleClass('active'); | |
if(action.hasClass('active')) | |
action.find('.action-options').find('.option a').eq(0).focus(); | |
// hide drop down if you click anywhere on the page | |
$(document).one('click', function() { | |
action.removeClass('active'); | |
}); | |
// event focus.actionLinks takes over from here | |
} | |
}); | |
$(document).on('focus.actionLinks', '.action-options .option a', function() { | |
var $this = $(this); | |
var option = $this.parent('.option'); | |
var previous = option.prev().find('a'); | |
var next = option.next().find('a'); | |
arrowKeys(); | |
function arrowKeys() { | |
$(document).one('keydown.actionLinkKeys', function(e){ | |
if(e.which === keymap.up) { | |
if(previous.length) { | |
e.preventDefault(); | |
previous.focus(); | |
} else { | |
e.preventDefault(); // stop the page from moving down/up | |
arrowKeys(); | |
} | |
} | |
if(e.which === keymap.down) { | |
if(next.length) { | |
e.preventDefault(); | |
next.focus(); | |
} else { | |
e.preventDefault(); // stop the page from moving down/up | |
arrowKeys(); | |
} | |
} | |
if(e.which === keymap.right) { | |
e.preventDefault(); | |
// $this.click(); | |
} | |
if(e.which === keymap.left) { | |
e.preventDefault(); | |
// option.parents('.action-group').removeClass('active'); | |
} | |
}); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DAR BE HORRIBLE, NASTY, NEVER DO THIS NORMALLY STUFF IN HERE, YAR! | |
// This must be placed at the top (or extremely close to the top) of ALL the javascript to keep ajax from continuing through and changing the page | |
$(document).on('focus', '.message-form .note textarea', function() { | |
var textarea = $(this); | |
$(document).one('click', 'a, input', function(e) { | |
if(textarea.val() !== '' && !$(this).hasClass('save-action')) { | |
if(!confirm('You have not submitted your note. Would you like to leave this page?')) { | |
e.preventDefault(); // don't follow the link | |
e.stopPropagation(); // don't allow other handlers to continue with ajax | |
} | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).on('keypress', '.date-field .control > label > input', function(e) { | |
if(e.which === 32) { // space key | |
// date values | |
var today = new Date(); | |
var month = today.getMonth()+1; | |
var day = today.getDate(); | |
var year = today.getFullYear(); | |
var parent = $(this).parents('.date-field'); | |
var $month = parent.find('.month input'); | |
var $day = parent.find('.day input'); | |
var $year = parent.find('.year input'); | |
$month.val(month); | |
$day.val(day); | |
$year.val(year); | |
var nextField = parent.next().find('input, select, textarea').eq(0); | |
while(nextField.length === 0) { | |
// we'll traverse the dom tree until we come to the next focusable input | |
parent = parent.parent(); | |
nextField = parent.next().find('input, select, textarea').eq(0); | |
} | |
nextField.focus(); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment