Skip to content

Kaikki WordPressistä, web-kehityksestä — ja paljon muuta

⚙️ Kuinka estää ei-toivotut merkkisyötteet INPUT-kentässä JavaScriptillä

⚙️ Kuinka estää ei-toivotut merkkisyötteet INPUT-kentässä JavaScriptillä

Rekisteröintilomake, puhelinkenttä, luettelohaku: käyttäjä klikkaa INPUT-kenttää ja kirjoittaa mitä tahansa. Kirjaimia sinne, missä odotat numeroita. Erikoismerkkejä tilausnumerossa. Emojeita sähköpostikentässä. Lopputulos: roskaa tietokannassa, rikkinäisiä kyselyitä, backendin validointi kaatuu virheeseen.

Ongelma ratkeaa frontendissa 15 koodirivillä. Ei liitännäisiä, ei kirjastoja, pelkkää JavaScriptiä tai pari HTML-attribuuttia. Alla on kolme toimivaa lähestymistapaa: nopeasta HTML5-ratkaisusta hienojakoiseen keydown-suodatukseen, valmiine esimerkkeineen yleisimpiin tilanteisiin.

💡 Pikakatsaus:

  • Tutkimme, milloin HTML-attribuutit pattern, inputmode ja type riittävät ja milloin tarvitaan JavaScriptiä
  • Kirjoitamme "vain numerot" -suodattimen jQuerylla ja puhtaalla JS:llä, molemmat versiot rivi riviltä selitettynä
  • Lisäämme suojauksen liittämistä vastaan Ctrl+V:n ja kontekstivalikon kautta
  • Rakennamme yleiskäyttöisen mallifunktion kaikenlaisiin rajoituksiin: kirjaimet, latinalaiset merkit, mukautetut maskit

HTML5: mitä voit tehdä ilman yhtäkään JavaScript-riviä

Yksinkertaisin ja luotettavin tapa rajoittaa syötettä on kertoa selaimelle, mitä kentässä tarkalleen odotetaan. Nykyselaimet ymmärtävät kolme keskeistä attribuuttia, jotka kattavat useimmat tavalliset tehtävät.

type, ensisijainen suodatin. type="number" sallii vain numerot, miinuksen ja desimaalierottimen. type="tel" näyttää mobiililaitteissa numeronäppäimistön. type="email" tarkistaa @-merkin lomakkeen lähetyksessä. type="url" vaatii protokollan. Tämä ei estä syöttämistä kokonaan, mutta tarjoaa oikean näyttönäppäimistön ja perusvalidoinnin lähetettäessä.

pattern, säännöllinen lauseke kentässä. Attribuutti hyväksyy regex-lausekkeen ja validoi arvon lomakkeen lähetyksessä:

1<input type="text" pattern="[0-9]{6}" placeholder="Six digits" title="Exactly 6 digits">

Selain estää lähetyksen, jos arvo ei vastaa lauseketta. Huono puoli: pattern ei estä kiellettyjen merkkien syöttämistä; se vain validoi lopputuloksen. Käyttäjä voi kirjoittaa "abc", ja näkee virheen vasta yrittäessään lähettää lomakkeen.

inputmode, oikea näppäimistö mobiilissa. inputmode="numeric" avaa numeronäppäimistön, mutta ei estä kirjainten syöttämistä. inputmode="decimal" lisää erottimen. Hyödyllinen yhdessä JavaScript-suodattimen kanssa: näppäimistö on jo oikea, ja JS hoitaa loput.

Mitä valita. Jos haluat todella estää kiellettyjen merkkien syöttämisen reaaliajassa, HTML-attribuutit eivät riitä. Siirrytään JavaScriptiin.

JavaScript: näppäinpainallusten esto keydownilla

keydown-lähestymistapa nappaa jokaisen näppäinpainalluksen ennen kuin merkki ilmestyy kenttään. Jos näppäin ei ole sallittujen listalla, event.preventDefault() estää sen.

Vaihtoehto 1: jQuery, vain numerot

Koodi kiinnitetään INPUT-kenttään id:n perusteella, sallii numerot ja toimintonäppäimet (backspace, nuolet, Tab) ja estää kaiken muun:

1$(document).ready(function() {
2 $("#phone_input").keydown(function(event) {
3 // Service keys — let them through
4 if (event.keyCode === 46 || event.keyCode === 8 ||
5 event.keyCode === 9 || event.keyCode === 27 ||
6 (event.keyCode === 65 && event.ctrlKey === true) ||
7 (event.keyCode >= 35 && event.keyCode <= 39)) {
8 return;
9 }
10 // Block everything except digits (main block and numpad)
11 if ((event.keyCode &lt; 48 || event.keyCode &gt; 57) &&
12 (event.keyCode &lt; 96 || event.keyCode &gt; 105)) {
13 event.preventDefault();
14 }
15 });
16});

Tässä mitä tapahtuu rivi riviltä: keyCode 46 (Delete), 8 (Backspace), 9 (Tab), 27 (Escape), 65 + Ctrl (valitse kaikki), 35..39 (Home, End, nuolet). Sitten tulevat päänumerolohko 48..57 (0..9) ja numeronäppäimistö 96..105. Mikään, mikä ei osu näihin väleihin, saa preventDefault-kutsun.

Täydellinen näppäinkooditaulukko Cambia Researchissa.

Vaihtoehto 2: puhdas JavaScript, vain numerot

Sama asia ilman jQuerya, käyttäen onkeypress-tapahtumaa. Tämä versio on tiiviimpi eikä vedä mukanaan kirjastoa:

1document.getElementById("phone_input").onkeypress = function(event) {
2 event = event || window.event;
3 if (event.charCode && (event.charCode &lt; 48 || event.charCode &gt; 57)) {
4 return false;
5 }
6};

charCode tarkistaa, että tapahtumalla on tulostuva merkki; toimintonäppäimet (nuolet, backspace) menevät läpi automaattisesti, koska niiden charCode on 0. Numerot: 48..57, kaikki muu palauttaa false.

Tärkeää: keyCode on merkitty vanhentuneeksi. Nykyaikaisessa koodissa on parempi käyttää event.key-ominaisuutta, joka palauttaa merkkijonon näppäimen nimellä eikä riipu näppäimistöasettelusta:

1element.addEventListener("keydown", function(event) {
2 // Allow digits, Backspace, Delete, arrows, Tab
3 const allowedKeys = [
4 "Backspace", "Delete", "Tab", "Escape",
5 "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown",
6 "Home", "End"
7 ];
8 if (allowedKeys.includes(event.key)) return;
9 // Ctrl+A / Ctrl+C / Ctrl+V
10 if (event.ctrlKey) return;
11 // Only digits
12 if (!/^[0-9]$/.test(event.key)) {
13 event.preventDefault();
14 }
15});

event.key-lähestymistapa on selkeälukuisempi eikä vaadi numerokoodien ulkoa opettelua. Mutta vanhempien selainten tukemiseen yllä oleva keyCode-versio toimii edelleen.

Liittämisen esto: Ctrl+V:n ja kontekstivalikon nappaaminen

keydown nappaa näppäinpainallukset, mutta käyttäjä voi liittää tekstiä Ctrl+V:llä tai hiiren oikealla napsautuksella → "Liitä". Hän kopioi "abc123" vain numeroita sallivaan kenttään, ja puolet siitä meni läpi.

Ratkaisu on input-tapahtuma. Se laukeaa MINKÄ tahansa kentän arvon muutoksen jälkeen: manuaalinen syöttö, liittäminen, automaattinen täyttö, tekstin raahaus ja pudotus. Suodatamme lopullisen arvon säännöllisellä lausekkeella:

1document.getElementById("digits_only").addEventListener("input", function(event) {
2 this.value = this.value.replace(/[^0-9]/g, "");
3});

Siinä kaikki. Yksi rivi, eikä yksikään ei-numero pääse kentän läpi, riippumatta siitä miten se sinne joutui. Kirjaimille: replace(/[^a-zA-Zа-яА-ЯёЁ]/g, ""). Latinalaisille merkeille: replace(/[^a-zA-Z]/g, "").

Yhdistetty lähestymistapa (kultainen keskitie). keydown antaa välittömän palautteen (näppäin "ei painu"), kun taas input suojaa liittämiseltä. Käytä molempia käsittelijöitä:

1const field = document.getElementById("digits_only");
2
3field.addEventListener("keydown", function(event) {
4 const allowed = ["Backspace", "Delete", "Tab", "Escape",
5 "ArrowLeft", "ArrowRight", "Home", "End"];
6 if (allowed.includes(event.key)) return;
7 if (event.ctrlKey) return;
8 if (!/^[0-9]$/.test(event.key)) event.preventDefault();
9});
10
11field.addEventListener("input", function() {
12 this.value = this.value.replace(/[^0-9]/g, "");
13});

Ensimmäinen käsittelijä katkaisee kielletyt näppäinpainallukset välittömästi. Toinen siivoaa jäänteet liittämisen ja automaattisen täytön jälkeen.

Valmis malli kaikenlaisiin rajoituksiin

Rakennetaan funktio, joka ottaa INPUT-kentän ja sallittujen merkkien säännöllisen lausekkeen. Se toimii puhelinnumeroille, postinumeroille, käyttäjätunnuksille, SKU-hauille:

1function restrictInput(element, allowedPattern) {
2 element.addEventListener("keydown", function(event) {
3 const serviceKeys = [
4 "Backspace", "Delete", "Tab", "Escape",
5 "ArrowLeft", "ArrowRight", "Home", "End"
6 ];
7 if (serviceKeys.includes(event.key)) return;
8 if (event.ctrlKey && ["a", "c", "v", "x", "z"].includes(event.key.toLowerCase())) return;
9 if (!allowedPattern.test(event.key)) event.preventDefault();
10 });
11
12 element.addEventListener("input", function() {
13 // Build a global pattern from the provided one and clean
14 const globalPattern = new RegExp(
15 allowedPattern.source.replace("^", "").replace("$", ""),
16 "g"
17 );
18 // Remove everything that does NOT match the pattern
19 const inverted = new RegExp(
20 "[^" + allowedPattern.source.slice(1, -1) + "]", "g"
21 );
22 this.value = this.value.replace(inverted, "");
23 });
24}

Käyttö:

1// Digits only
2restrictInput(document.getElementById("phone"), /^[0-9]$/);
3// Latin letters only without spaces
4restrictInput(document.getElementById("login"), /^[a-zA-Z]$/);
5// Letters and digits
6restrictInput(document.getElementById("postal"), /^[a-zA-Z0-9]$/);

Funktio ei riipu jQuerysta, toimii kaikissa nykyselaimissa ja kattaa kolme skenaariota: manuaalinen syöttö (keydown), liittäminen (input), automaattinen täyttö (input).

Huom: keydown ei laukea virtuaalinäppäimistöillä joissakin mobiililaitteissa; siellä input jää ainoaksi suojaksi. Siksi kaksi käsittelijää yhdessä ei ole redundanssia vaan välttämättömyys.

Yllä oleva video on erittely lomakkeen validoinnista puhtaalla JavaScriptillä Web Dev Simplifiedilta. Se kattaa sisäänrakennetun Constraint Validation API:n, mukautetut virheilmoitukset ja virheellisten kenttien tyylittelyn, loistava täydennys tässä kuvatuille lähestymistavoille.

⁉️🤔 Usein kysytyt kysymykset

Miksei vain käyttäisi type="number"-attribuuttia?

type="number" sallii e- ja E-merkkien (eksponentti), plussan, miinuksen ja pisteen syöttämisen. Kaikki nämä ovat kelvollisia merkkejä liukuluvuille ja eksponenttimuodolle. Puhelinkenttään tai postinumeroon ne ovat tarpeettomia. Lisäksi selaimet käsittelevät ei-numeerista syötettä eri tavoin: jotkut päästävät sen hiljaa läpi, toiset näyttävät virheen vasta lähetettäessä.

Voiko liittämisen Ctrl+V:llä estää kokonaan?

Liittämisen täydellinen estäminen on huono käytäntö. Se rikkoo käyttökokemuksen eikä suojaa muita liittämistapoja vastaan (kontekstivalikko, raahaus ja pudotus). Oikea lähestymistapa: salli liittäminen, mutta siivoa tulos välittömästi input-käsittelijällä, kuten yllä olevassa osiossa näytettiin.

Entä selaimen automaattinen täyttö?

Automaattinen täyttö laukeaa ennen keydown- ja input-tapahtumia; selain lisää arvon suoraan. input-käsittelijä nappaa tämänkin tapauksen: arvo ilmestyy kenttään, sitten se suodatetaan välittömästi. Jos automaattinen täyttö häiritsee (lisää kirjaimia puhelinkenttään), lisää autocomplete="off"-attribuutti INPUT-kenttään.

Toimiiko tämä mobiililaitteilla?

Kyllä, mutta yhdellä varauksella: virtuaalinäppäimistöt eivät aina tuota keydown-tapahtumaa odotetuilla keyCode- ja key-arvoilla. input-käsittelijä toimii luotettavasti kaikkialla. Mobiiilissa aseta INPUT-kenttään inputmode="numeric" tai inputmode="decimal"; käyttäjä saa oikean näppäimistön, vaikka keydown-suodatin ei laukeaisikaan.

Miten välilyönnit estetään?

Lisää välilyönti kiellettyjen merkkien luokkaan: replace(/[^0-9a-zA-Z]/g, "") poistaa kaiken paitsi numerot ja latinalaiset kirjaimet, mukaan lukien välilyönnit. Tai eksplisiittisesti: this.value = this.value.replace(/\s/g, "") poistaa kaikki välilyönnit, sarkaimet ja rivinvaihdot.

Mikä lähestymistapa valita omaan tehtävään

Ohjelmistokehittäjä työpöydän ääressä koodi näytöllä

Jos lomake on yksinkertainen eivätkä pari väärää merkkiä aiheuta kriittistä vahinkoa, käytä HTML5:tä: type, pattern ja inputmode kattavat perusskenaariot ilman yhtäkään JS-riviä. Nopea, natiivi, ei riippuvuuksia.

Jos reaaliaikainen hallinta ja välitön reaktio jokaiseen näppäinpainallukseen on tärkeää, käytä keydown + input -yhdistelmää. Ensimmäinen estää näppäinpainallukset, toinen siivoaa liittämisen seuraukset. Yllä olevan osion yleiskäyttöinen mallifunktio mukautuu mihin tahansa kenttään minuutissa.

Pääsääntö: älä estä liittämistä kokonaan. Se ärsyttää käyttäjiä eikä tarjoa todellista suojaa. Siivoa tulos liittämisen jälkeen sen kieltämisen sijaan.