Open GoogleCodeExporter opened 8 years ago
Appears on Firefox 3.5.6 to be more specific
Original comment by viliam.h...@gmail.com
on 24 Feb 2010 at 4:10
also experienced this issue on chrome 4.0.249.89 with jQuery 1.4.2 minified +
js-hotkeys 0.7.9 minified
Original comment by jebaird
on 25 Feb 2010 at 8:11
John Resig JQuery's creator has fork and fix these problems.
check it out at http://github.com/jeresig/jquery.hotkeys/
Original comment by Afro.Sys...@gmail.com
on 26 Feb 2010 at 5:02
Resig's version doesn't fix my problems. I start with a ctrl+m macro, and if I
add another macro, it stops working.
If I use Resig's version instead, it doesn't work at all either way. Using
jQuery 1.4.2, and this happens in Chrome
and Firefox at least.
Original comment by thebroke...@gmail.com
on 19 Apr 2010 at 6:16
@thebrokenladder in that case, I'l check this soon and get back to you with
answers.
Original comment by Afro.Sys...@gmail.com
on 20 Apr 2010 at 3:23
If i use Resig's version, i lose the ability to have an textbox as the focus
and still
use the hotkeys, any chance you could do up the changes like Resig did but
still keep
the ability to use this on a textbox even with bindings like:
$(document).bind('keydown','f2',function myFun(event) { }));???
Original comment by ray73...@gmail.com
on 10 May 2010 at 4:55
As of aug8, bug exists on firefox 3.6.8 and chrome 5.0.375. Resig's version
removes the double triggering on firefox. But it still exists on chrome. It
looks like a bounded event for a hotkey is trigged as many times as there are
js-hotkey binded events.
Original comment by tejeshwa...@gmail.com
on 8 Aug 2010 at 10:02
Bug confirmed in Safari Mac 5.0 (6533.16), Firefox Mac 3.5.4, Chrome Mac
5.0.375.125.
Both in 0.7.9 full and 0.7.9 minified together with jquery 1.4.2 full.
Confirmed that the event is triggered as many times as there are events of the
same type (i.e. 'keydown', 'keyup') bound to the same object.
Workaround/ugly fix: bind the event to different objects, i.e.
$(document).bind(....) and $('body').bind(....)
Original comment by tako.bru...@gmail.com
on 10 Aug 2010 at 7:05
[deleted comment]
Hey guys I have fixed the problem of multiple bind problem as well as unbinding
issue. There is a sinple workarround for this I have added the code below. This
code is tested with Firefox and Internet Explorer and its working fine for
thos, not tested for other browser's. Let me know if you guys find any problem
in other browser's as well if you face any problem.
Here is the code:
/*
(c) Copyrights 2007 - 2008
Original idea by by Binny V A,
http://www.openjs.com/scripts/events/keyboard_shortcuts/
jQuery Plugin by Tzury Bar Yochay
tzury.by@gmail.com
http://evalinux.wordpress.com
http://facebook.com/profile.php?id=513676303
Project's sites:
http://code.google.com/p/js-hotkeys/
http://github.com/tzuryby/hotkeys/tree/master
License: same as jQuery license.
USAGE:
// simple usage
$(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
// special options such as disableInIput
$(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
Note:
This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
*/
(function (jQuery){
// keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
jQuery.fn.__bind__ = jQuery.fn.bind;
jQuery.fn.__unbind__ = jQuery.fn.unbind;
jQuery.fn.__find__ = jQuery.fn.find;
var hotkeys = {
version: '0.7.9',
override: /keypress|keydown|keyup/g,
triggersMap: {},
specialKeys: {
27: 'esc',
9: 'tab',
32:'space',
13: 'return',
8:'backspace',
145: 'scroll',
20: 'capslock',
144: 'numlock',
19:'pause',
45:'insert',
36:'home',
46:'del',
35:'end',
33: 'pageup',
34:'pagedown',
37:'left',
38:'up',
39:'right',
40:'down',
109: '-',
112:'f1',
113:'f2',
114:'f3',
115:'f4',
116:'f5',
117:'f6',
118:'f7',
119:'f8',
120:'f9',
121:'f10',
122:'f11',
123:'f12',
191: '/'
},
shiftNums: {
"`":"~",
"1":"!",
"2":"@",
"3":"#",
"4":"$",
"5":"%",
"6":"^",
"7":"&",
"8":"*",
"9":"(",
"0":")",
"-":"_",
"=":"+",
";":":",
"'":"\"",
",":"<",
".":">",
"/":"?",
"\\":"|"
},
newTrigger: function (type, combi, callback) {
// i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
var result = {};
result[type] = {};
result[type][combi] = {
cb: callback,
disableInInput: false
};
return result;
}
};
// add firefox num pad char codes
//if (jQuery.browser.mozilla){
// add num pad char codes
hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, {
96: '0',
97:'1',
98: '2',
99:
'3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111 : '/'
});
//}
// a wrapper around of $.fn.find
// see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
jQuery.fn.find = function( selector ) {
this.query = selector;
return jQuery.fn.__find__.apply(this, arguments);
};
jQuery.fn.unbind = function (type, combi, fn){
if (jQuery.isFunction(combi)){
fn = combi;
combi = null;
}
if (combi && typeof combi === 'string'){
var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
var hkTypes = type.split(' ');
for (var x=0; x<hkTypes.length; x++){
delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi.toLowerCase()];
}
}
// call jQuery original unbind
return this.__unbind__(type, fn);
};
var flag = false;
jQuery.fn.bind = function(type, data, fn){
// grab keyup,keydown,keypress
var handle = type.match(hotkeys.override);
if (jQuery.isFunction(data) || !handle){
// call jQuery.bind only
return this.__bind__(type, data, fn);
}
else{
// split the job
var result = null,
// pass the rest to the original $.fn.bind
pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
// see if there are other types, pass them to the original $.fn.bind
if (pass2jq){
result = this.__bind__(pass2jq, data, fn);
}
if (typeof data === "string"){
data = {
'combi': data
};
}
if(data.combi){
for (var x=0; x < handle.length; x++){
var eventType = handle[x];
var combi = data.combi.toLowerCase(),
trigger = hotkeys.newTrigger(eventType, combi, fn),
selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
//trigger[eventType][combi].propagate = data.propagate;
trigger[eventType][combi].disableInInput = data.disableInInput;
// first time selector is bounded
if (!hotkeys.triggersMap[selectorId]) {
hotkeys.triggersMap[selectorId] = trigger;
}
// first time selector is bounded with this type
else if (!hotkeys.triggersMap[selectorId][eventType]) {
hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
}
// make trigger point as array so more than one handler can be bound
var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
if (!mapPoint){
hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
}
else if (mapPoint.constructor !== Array){
hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
}
else {
hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
}
// add attribute and call $.event.add per matched element
this.each(function(){
// jQuery wrapper for the current element
var jqElem = jQuery(this);
// element already associated with another collection
if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
selectorId = jqElem.attr('hkId') + ";" + selectorId;
}
jqElem.attr('hkId', selectorId);
});
if(!flag)
{
flag = true;
result = this.__bind__(handle.join(' '), data, hotkeys.handler)
}
}
}
return result;
}
};
// work-around for opera and safari where (sometimes) the target is the element which was last
// clicked with the mouse and not the document event it would make sense to get the document
hotkeys.findElement = function (elem){
if (!jQuery(elem).attr('hkId')){
if (jQuery.browser.opera || jQuery.browser.safari){
while (!jQuery(elem).attr('hkId') && elem.parentNode){
elem = elem.parentNode;
}
}
}
return elem;
};
// the event handler
hotkeys.handler = function(event) {
var target = hotkeys.findElement(event.currentTarget),
jTarget = jQuery(target),
ids = jTarget.attr('hkId');
if(ids){
ids = ids.split(';');
var code = event.which,
type = event.type,
special = hotkeys.specialKeys[code],
// prevent f5 overlapping with 't' (or f4 with 's', etc.)
character = !special && String.fromCharCode(code).toLowerCase(),
shift = event.shiftKey,
ctrl = event.ctrlKey,
// patch for jquery 1.2.5 && 1.2.6 see more at:
// http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
alt = event.altKey || event.originalEvent.altKey,
mapPoint = null;
for (var x=0; x < ids.length; x++){
if (hotkeys.triggersMap[ids[x]][type]){
mapPoint = hotkeys.triggersMap[ids[x]][type];
break;
}
}
//find by: id.type.combi.options
if (mapPoint){
var trigger;
// event type is associated with the hkId
if(!shift && !ctrl && !alt) { // No Modifiers
trigger = mapPoint[special] || (character && mapPoint[character]);
}
else{
// check combinations (alt|ctrl|shift+anything)
var modif = '';
if(alt) modif +='alt+';
if(ctrl) modif+= 'ctrl+';
if(shift) modif += 'shift+';
// modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
trigger = mapPoint[modif+special];
if (!trigger){
if (character){
trigger = mapPoint[modif+character]
|| mapPoint[modif+hotkeys.shiftNums[character]]
// '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
|| (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
}
}
}
if (trigger){
var result = false;
for (var x=0; x < trigger.length; x++){
if(trigger[x].disableInInput){
// double check event.currentTarget and event.target
var elem = jQuery(event.target);
if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select")
|| elem.is("input") || elem.is("textarea") || elem.is("select")) {
return true;
}
}
// call the registered callback function
result = result || trigger[x].cb.apply(this, [event]);
}
return result;
}
}
}
return result;
};
// place it under window so it can be extended and overridden by others
window.hotkeys = hotkeys;
return jQuery;
})(jQuery);
Original comment by sandeep....@gmail.com
on 7 Feb 2011 at 12:16
Above rewritten plugin doesn't work for me.
I made workaround, blocking execution of binded function for one second:
var keypressed = '';
$(iframeId).contents().bind('keypress', 'ctrl+u', function(){
timestamp = Math.floor(new Date().getTime()/1000);
if(keypressed == timestamp) {return false;}else {
keypressed = timestamp;
formatText('underline', rteName);
return false;
}
})
It is not good solution - accidentaly if user press key near end of second it
may be triggered twice.
Original comment by kolibe...@gmail.com
on 28 Apr 2011 at 10:31
Version 0.8 working great using jquery 1.6.2. Tested on Chrome 13 and FF 5.0
and IE 9
Original comment by aprudencio
on 1 Sep 2011 at 5:08
Hello Guys,
Hey guys I have fixed the problem of multiple bind problem as well as unbinding
issue. There is a sinple workarround for this I have added the code below. This
code is tested with Firefox and Internet Explorer and its working fine for
thos, not tested for other browser's. Let me know if you guys find any problem
in other browser's as well if you face any problem.
Here is the code:
/*
(c) Copyrights 2007 - 2008
Original idea by by Binny V A,
http://www.openjs.com/scripts/events/keyboard_shortcuts/
jQuery Plugin by Tzury Bar Yochay
tzury.by@gmail.com
http://evalinux.wordpress.com
http://facebook.com/profile.php?id=513676303
Project's sites:
http://code.google.com/p/js-hotkeys/
http://github.com/tzuryby/hotkeys/tree/master
License: same as jQuery license.
USAGE:
// simple usage
$(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
// special options such as disableInIput
$(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
Note:
This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
*/
(function (jQuery){
// keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
jQuery.fn.__bind__ = jQuery.fn.bind;
jQuery.fn.__unbind__ = jQuery.fn.unbind;
jQuery.fn.__find__ = jQuery.fn.find;
var hotkeys = {
version: '0.7.9',
override: /keypress|keydown|keyup/g,
triggersMap: {},
specialKeys: {
27: 'esc',
9: 'tab',
32:'space',
13: 'return',
8:'backspace',
145: 'scroll',
20: 'capslock',
144: 'numlock',
19:'pause',
45:'insert',
36:'home',
46:'del',
35:'end',
33: 'pageup',
34:'pagedown',
37:'left',
38:'up',
39:'right',
40:'down',
109: '-',
112:'f1',
113:'f2',
114:'f3',
115:'f4',
116:'f5',
117:'f6',
118:'f7',
119:'f8',
120:'f9',
121:'f10',
122:'f11',
123:'f12',
191: '/'
},
shiftNums: {
"`":"~",
"1":"!",
"2":"@",
"3":"#",
"4":"$",
"5":"%",
"6":"^",
"7":"&",
"8":"*",
"9":"(",
"0":")",
"-":"_",
"=":"+",
";":":",
"'":"\"",
",":"<",
".":">",
"/":"?",
"\\":"|"
},
newTrigger: function (type, combi, callback) {
// i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
var result = {};
result[type] = {};
result[type][combi] = {
cb: callback,
disableInInput: false
};
return result;
}
};
// add firefox num pad char codes
//if (jQuery.browser.mozilla){
// add num pad char codes
hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, {
96: '0',
97:'1',
98: '2',
99:
'3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111 : '/'
});
//}
// a wrapper around of $.fn.find
// see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
jQuery.fn.find = function( selector ) {
this.query = selector;
return jQuery.fn.__find__.apply(this, arguments);
};
jQuery.fn.unbind = function (type, combi, fn){
if (jQuery.isFunction(combi)){
fn = combi;
combi = null;
}
if (combi && typeof combi === 'string'){
var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
var hkTypes = type.split(' ');
for (var x=0; x<hkTypes.length; x++){
delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi.toLowerCase()];
}
}
// call jQuery original unbind
return this.__unbind__(type, fn);
};
var flag = false;
jQuery.fn.bind = function(type, data, fn){
// grab keyup,keydown,keypress
var handle = type.match(hotkeys.override);
if (jQuery.isFunction(data) || !handle){
// call jQuery.bind only
return this.__bind__(type, data, fn);
}
else{
// split the job
var result = null,
// pass the rest to the original $.fn.bind
pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
// see if there are other types, pass them to the original $.fn.bind
if (pass2jq){
result = this.__bind__(pass2jq, data, fn);
}
if (typeof data === "string"){
data = {
'combi': data
};
}
if(data.combi){
for (var x=0; x < handle.length; x++){
var eventType = handle[x];
var combi = data.combi.toLowerCase(),
trigger = hotkeys.newTrigger(eventType, combi, fn),
selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
//trigger[eventType][combi].propagate = data.propagate;
trigger[eventType][combi].disableInInput = data.disableInInput;
// first time selector is bounded
if (!hotkeys.triggersMap[selectorId]) {
hotkeys.triggersMap[selectorId] = trigger;
}
// first time selector is bounded with this type
else if (!hotkeys.triggersMap[selectorId][eventType]) {
hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
}
// make trigger point as array so more than one handler can be bound
var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
if (!mapPoint){
hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
}
else if (mapPoint.constructor !== Array){
hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
}
else {
hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
}
// add attribute and call $.event.add per matched element
this.each(function(){
// jQuery wrapper for the current element
var jqElem = jQuery(this);
// element already associated with another collection
if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
selectorId = jqElem.attr('hkId') + ";" + selectorId;
}
jqElem.attr('hkId', selectorId);
});
if(!flag)
{
flag = true;
result = this.__bind__(handle.join(' '), data, hotkeys.handler)
}
}
}
return result;
}
};
// work-around for opera and safari where (sometimes) the target is the element which was last
// clicked with the mouse and not the document event it would make sense to get the document
hotkeys.findElement = function (elem){
if (!jQuery(elem).attr('hkId')){
if (jQuery.browser.opera || jQuery.browser.safari){
while (!jQuery(elem).attr('hkId') && elem.parentNode){
elem = elem.parentNode;
}
}
}
return elem;
};
// the event handler
hotkeys.handler = function(event) {
var target = hotkeys.findElement(event.currentTarget),
jTarget = jQuery(target),
ids = jTarget.attr('hkId');
if(ids){
ids = ids.split(';');
var code = event.which,
type = event.type,
special = hotkeys.specialKeys[code],
// prevent f5 overlapping with 't' (or f4 with 's', etc.)
character = !special && String.fromCharCode(code).toLowerCase(),
shift = event.shiftKey,
ctrl = event.ctrlKey,
// patch for jquery 1.2.5 && 1.2.6 see more at:
// http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
alt = event.altKey || event.originalEvent.altKey,
mapPoint = null;
for (var x=0; x < ids.length; x++){
if (hotkeys.triggersMap[ids[x]][type]){
mapPoint = hotkeys.triggersMap[ids[x]][type];
break;
}
}
//find by: id.type.combi.options
if (mapPoint){
var trigger;
// event type is associated with the hkId
if(!shift && !ctrl && !alt) { // No Modifiers
trigger = mapPoint[special] || (character && mapPoint[character]);
}
else{
// check combinations (alt|ctrl|shift+anything)
var modif = '';
if(alt) modif +='alt+';
if(ctrl) modif+= 'ctrl+';
if(shift) modif += 'shift+';
// modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
trigger = mapPoint[modif+special];
if (!trigger){
if (character){
trigger = mapPoint[modif+character]
|| mapPoint[modif+hotkeys.shiftNums[character]]
// '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
|| (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
}
}
}
if (trigger){
var result = false;
for (var x=0; x < trigger.length; x++){
if(trigger[x].disableInInput){
// double check event.currentTarget and event.target
var elem = jQuery(event.target);
if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select")
|| elem.is("input") || elem.is("textarea") || elem.is("select")) {
return true;
}
}
// call the registered callback function
result = result || trigger[x].cb.apply(this, [event]);
}
return result;
}
}
}
return result;
};
// place it under window so it can be extended and overridden by others
window.hotkeys = hotkeys;
return jQuery;
})(jQuery);
This code working fine with and resolved isssue.
Thanks
Original comment by anciwa...@gmail.com
on 1 Dec 2011 at 12:34
Original issue reported on code.google.com by
viliam.h...@gmail.com
on 24 Feb 2010 at 4:09