6 ноября 2011 г.

"Unexpected token u" в Chrome


Столкнулся в Chrome (v14) с таким сообщением ошибки:
"Uncaught SyntaxError: Unexpected token u"
Проблема проста особенно для языков с динамической типизацией, на вход метода JSON.parse попала undefined.
Обычно такое сообщение возникает, когда в исходном тексте попадаются лишние символы. Но в данном примере стандартный метод просто решил выкинуть ошибку с невнятным описанием, вместо того, чтобы вернуть undefined. Баг в Chrome?

Ошибку можно с имитировать:
var demo1={field1:"a",field2:"b"};
var str=JSON.stringify(demo1);
console.log(str);
console.dir(JSON.parse(str)); // Ok
console.dir(JSON.parse(undefined)); // Exception

4 ноября 2011 г.

События keydown и keypress в Javascript



По работе в плотную встретился с двумя этими событиями, и хочу поделиться своим опытом работы с ними. Заодно приведу пример использования этих событий.
Дело осложнаятся тем, что нету стандарта для событий нажатий клавиш, и поэтому имеются рассхождения в работе в разных браузерах. События keydown и keypress в объекте события могут содержать несколько свойств, характеризующих нажатую клавишу:
  • charCode - это unicode значение символа связанного с нажатой клавишей;
  • keyCode - код клавиш;
  • which - идентичен charCode.
Для пояснения приведу пример - вводимые символы 'a' и 'A' будут иметь одинаковый keyCode, но разыный charCode.
Событие keyPress служит для обработки печатных клавиш (printable characters), т.е. те которые имеют печатные символы: A-Z, 0-9, Enter, Backspace.
Но есть один неприятный момент с этим событием - по своему назначению работет только в IE и Chrome(Webkit браузерах). В FireFox и Opera объект события возвращает коды  и клавиш с непечатными символами (Non printable characters) - клавиша Del, стрелочки. Причем коды для печатных и непечатных символов могут совпадать.
Событие keyDown - объект события "отлавливает" как коды печатных символов, так и не печатных символов. При этом код каждой клавиши будет уникальным.
Подробнее про эти два события можно почитать на  http://www.quirksmode.org/js/keys.html

Варианты значений свойств объекта события keypress для разных браузеров:
Chrome 15/Safari 5.1.1
Примечание: N- целое число не равное нулю.
Клавиши keyCode charCode which
Printable characters N N N
Non pintable characters - - -
Opera 11.52:
Клавиши keyCode charCode which
Printable characters N undefined N
Non pintable characters N undefined 0
FireFox 7:
Клавиши keyCode charCode which
Printable characters 0 N N
Non pintable characters N 0 0
IE6/7/8/9:
Клавиши keyCode charCode which
Printable characters N undefined undefined
Non pintable characters - - -

Привожу несколько примеров работы с событиями keypress и keydown.
Последний пример где производится проверка ввода - а именно в поле разрешается вводить только вещественное положительное число, имеет небольшой недостаток при использование. Текст можно ввести не только с клавиаткры, но и вставить из буфера обмена и подстановки самого браузера.
JS код:
Copy Source | Copy HTML
  1. window.onload=function(){
  2.     var tst1=id("tst1"),
  3.         dbg1=id("dbg1"),
  4.         tst2=id("tst2"),
  5.         dbg2=id("dbg2");
  6.     event(tst1,"keypress",function(e){
  7.         var nonPrintable,printable; // параметры с помощью которых попытаемся решить проблему с кросс браузерностью
  8.         if((e.charCode=== 0||e.charCode===undefined)&&e.which=== 0)
  9.             nonPrintable=e.keyCode;
  10.         printable=e.which?e.which:(e.which===undefined&&e.charCode===undefined)?e.keyCode:undefined;
  11.         dbg1.innerHTML="keyCode: "+e.keyCode+", charCode: "+e.charCode+", which: "+e.which+
  12.         " #### caf: "+printable+", yk: "+nonPrintable;
  13.     });
  14.     event(tst2,"keydown",function(e){
  15.         dbg2.innerHTML="keyCode: "+e.keyCode+", charCode: "+e.charCode+", which: "+e.which;
  16.     });
  17.     var num1=id("num1");
  18.     // тегу input добавлем проверку ввода
  19.     event(num1,"keydown",function(e){
  20.         e = (e)?e: (window.event)?window.event:undefined;
  21.         var target;
  22.         if(e) {
  23.             target = (e.target)?e.target:e.srcElement;
  24.         }
  25.         console.dir(e);
  26.         if (e.keyCode===13){// Enter if need
  27.             e.preventDefault? e.preventDefault() : e.returnValue = false;
  28.         }
  29.         if ((e.keyCode>47&&e.keyCode<58)||e.keyCode===8||e.keyCode===37||e.keyCode===39||e.keyCode==46){
  30.             e.returnValue=true; // [0..9] -> [47..58], 8 - backsace, 37 - Left, 39 - Right, 46 -Delete
  31.         }else if (e.keyCode===188 || e.keyCode===190){
  32.             var s=target.value, // input field!
  33.                     i= 0,count= 0;
  34.             while (s[i]){
  35.                 if (s[i]==='.' || s[i]===',')
  36.                     count++;
  37.                 i++;
  38.             }
  39.             e.returnValue=count=== 0;// Точка или запятая может быть только одна в водимом числе
  40.             if(count!== 0 && e.preventDefault)// Condition for FireFox
  41.                 e.preventDefault();
  42.         }else{
  43.             e.preventDefault? e.preventDefault() : e.returnValue = false;
  44.         }
  45.     });
  46. };
  47. function id(s){
  48.     return document.getElementById(s);
  49. }
  50. function event(obj,name,handler){
  51.     if (obj.attachEvent){
  52.         obj.attachEvent("on"+name,handler);
  53.     }else{
  54.         obj.addEventListener(name,handler);
  55.     }
  56. }
html код:
Copy Source | Copy HTML
  1. <p id="tst1" contenteditable="true">Пример на событие keypress</p>
  2. <p id="dbg1"></p>
  3. <p id="tst2" contenteditable="true">Пример на событие keydown</p>
  4. <p id="dbg2"></p>
  5. <p>Пример - Введите число 1: <input type="text" id="num1"/></p>