Jak zjistit, zda stisknutá klávesa vytvoří znak uvnitř textového pole?

Mám běžné textové pole:

<input type="text"> 

Ke zpracování událostí souvisejících s klíči používám jQuery:

$("input:text").keydown(function() {
    // keydown code
}).keypress(function() {
    // keypress code
}).keyup(function() {
    // keyup code
});

Uživatel se soustředí na textové pole a mačká různé klávesy na klávesnici (obvyklé:písmena, čísla, SHIFT, BACKSPACE, MEZERNÍK, …). Potřebuji zjistit, kdy uživatel stiskne klávesu, která prodlouží délku hodnoty textového pole. Například klávesa „A“ ji zvýší, klávesa „SHIFT“ ne.

Pamatuji si, jak jsem sledoval přednášku PPK, kde zmínil rozdíl mezi těmito dvěma. Má to něco společného s událostí – keydown vs. keypress – a možná s vlastnostmi události – key, char, keyCode.

Aktualizovat!

Potřebuji tyto informace znát v obslužných programech pro stisk klávesy nebo stisknutí klávesy. Nemohu se dočkat, až dojde k události keyup.

Proč to potřebuji:

Mám textové pole, jehož velikost se dynamicky mění na základě vstupu uživatele. Můžete se podívat na toto demo:http://vidasp.net/tinydemos/variable-size-text-box.html

V ukázce mám handler keydown a keyup. Obslužný program keyup upravuje velikost textového pole na základě vstupní hodnoty. Obslužný program keydown však nastaví velikost tak, aby byla o 1 znak větší než vstupní hodnota. Důvod, proč to dělám, je ten, že kdybych to neudělal, pak by znak přetekl mimo textové pole a pouze když uživatel pustil klávesu, textové pole by se rozšířilo. Tohle vypadá divně. Proto musím nový znak předvídat – zvětšuji textové pole při každém stisknutí klávesy, ergo, než se znak objeví v textovém poli. Jak můžete vidět v ukázce, tato metoda vypadá skvěle.

Problémem jsou ale klávesy BACKSPACE a ARROW – ty také rozbalí textové pole při klávese dolů a pouze při stisknutí klávesy se velikost textového pole opraví.

Řešení:

Řešením by bylo detekovat klávesy BACKSPACE, SHIFT a ARROW ručně a na základě toho jednat:

// keydown handler
function(e) {
    var len = $(this).val().length;
    if (e.keyCode === 37 || e.keyCode === 39 ||
        e.keyCode === 16) { // ARROW LEFT or ARROW RIGHT or SHIFT key
        return;
    } else if (e.keyCode === 8) { // BACKSPACE key
        $(this).attr("size", len <= 1 ? 1 : len - 1);
    } else {
        $(this).attr("size", len === 0 ? 1 : len + 1);
    }
}

Toto funguje (a vypadá skvěle) pro BACKSPACE, SHIFT, ARROW LEFT a ARROW RIGHT. Chtěl bych však mít robustnější řešení.

Odpověď

Myslím, že to bude dělat svou práci, nebo pokud ne, je to velmi blízko a bude potřebovat pouze drobné úpravy. Věc, kterou si musíte zapamatovat, je, že nemůžete spolehlivě říci vůbec nic o žádném znaku, který může být napsán v keydown nebo keyup událost:že vše musí být provedeno v keypress psovod. Konečným zdrojem pro klíčové události je http://unixpapa.com/js/key.html

Musíte také zvážit pasty, se kterými si tento kód neporadí. Budete muset mít samostatný paste obsluha události (ačkoli tato událost není podporována v prohlížečích Firefox <3.0, Opera a velmi staré prohlížeče WebKit). V obslužném programu vkládání budete potřebovat časovač, protože v JavaScriptu není možné získat přístup k obsahu, který se chystáte vložit.

function isCharacterKeyPress(evt) {
    if (typeof evt.which == "undefined") {
        // This is IE, which only fires keypress events for printable keys
        return true;
    } else if (typeof evt.which == "number" && evt.which > 0) {
        // In other browsers except old versions of WebKit, evt.which is
        // only greater than zero if the keypress is a printable key.
        // We need to filter out backspace and ctrl/alt/meta key combinations
        return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8;
    }
    return false;
}

<input type="text" onkeypress="alert(isCharacterKeyPress(event))">