Как да открием последователност от натискания на клавиши в JavaScript

Снимка на Александър Цветанович на Unsplash

Една от най-използваните функции на JavaScript е способността му да реагира на различни събития, които могат да възникнат, докато потребителят взаимодейства с уеб страницата. Всъщност това беше самата идея на JavaScript, когато се появи, да направи уеб страниците динамични, като им добавите известна интерактивност. С JavaScript можем да реагираме, когато потребителят кликне върху определена част от страницата, когато е натиснат клавиш, когато мишката се движи, кога страницата е заредена, когато елемент се фокусира и т.н.

За списъка на всички налични JavaScript събития проверете тази MDN страница.

Въпреки че днес JavaScript може да се използва за много повече, тази основна функция за добавяне на интерактивност към уеб страницата все още се използва широко, за да предостави на потребителите богати и интересни изживявания.

В тази статия ще разгледаме как можем да използваме JavaScript, за да реагираме на клавиатурните събития, по-специално как да реагираме на специфичната последователност на клавишите, която потребителят въвежда. Така че, когато потребителят натисне клавишна комбинация, уеб страницата може да показва някакво съдържание, като например отваряне на меню или модал, тя може да промени стила на страницата или да извърши всяко друго действие, което можете да си представите, което е в рамките на възможностите на JavaScript.

Преди да започнем, просто искам да отбележа, че използвам някои функции на ES6 като const and let, оператора на разпространение (...), стрелките и т.н. Ако не сте запознати с тях, проверете предоставените връзки, за да научите Повече ▼.

Какво строим?

За тази статия реших да накарам елемент на страницата да променя фоновото си изображение въз основа на последователността на клавишите, които потребителите използват. Така че ние основно се занимаваме със стайлинга, нищо прекалено фантастично. Ще добавим и някои текстови актуализации, само за да направим демонстрацията по-богата и да предоставим повече информация на потребителя.

Идеята ми беше да използвам ключовите последователности, които се използват като мамят кодове в стара FPS игра, Doom и Doom 2. По принцип, когато потребителят въведе ключова комбинация, играта дава някои ползи за потребителя, като например повече боеприпаси, повече здраве , неуязвимост и т.н.

В примера, който ще изградим, ще направим фоновото изображение за елемент на страницата да се промени въз основа на последователността на ключовете, въведена от потребителя. Можете да видите и опитате демонстрацията тук.

Основен проект

За да се съсредоточа само върху JavaScript, създадох пълен HTML и CSS за проекта и свързах JavaScript файла, който съдържа един конзолен дневник, за да се уверя, че той работи. Можете да изтеглите първоначалните файлове на проекта тук или можете да създадете свои собствени, ако искате да го направите по свой начин.

Ето бърза разбивка на съдържанието на JavaScript, което първоначално имаме.

Това е доста просто, добавяме слушател на събитие към документа, който чака докато DOM бъде напълно зареден и след това се обажда на функцията за обратно извикване. Понастоящем функцията за обратно извикване има само декларация „използвай строго“ и дневник на конзолата, за да провери дали файлът е правилно свързан към HTML страницата.

Можем да пропуснем слушателя на това събитие и обратния разговор, който се предава към него, но след това файлът със скрипт трябва да бъде поставен в края на HTML елемента на тялото, за да се увери, че DOM е напълно зареден, когато javascript започне да изпълнява. Ако решите така, не се колебайте да го направите. Това е само моят предпочитан начин да го направя, с предимството, че той също така капсулира целия JavaScript код във функция и избягва замърсяването в глобален обхват.

Слушане на ключовите събития

Следващата стъпка ще бъде да открие кога потребителят натисне клавиш. Правим това по същия начин, по който добавихме слушателя за събитието DOMContentLoaded, но сега ще уточним различно име на събитието. Има три събития на клавиатурата, които можем да определим: клавиатура, клавиатура и натискане на клавиш.

  • клавиш - задейства се веднага след натискане на клавиша
  • keyup - задейства се при освобождаване на ключа
  • натискане на клавиш - стартира непрекъснато, докато клавишът се държи натиснат

От това описание можем веднага да видим, че натискането на клавиши определено не е подходящо за нашата цел. Това ни оставя с клавиатура и клавиатура. В нашия случай и двете ще се справят добре, но в различна ситуация един от тях може да е по-подходящ от другия, така че ще трябва да прецените ситуацията и да изберете съответно.

Единствената разлика, която можем да забележим тук, е, че ако използваме клавиатурата, тя може да се почувства леко закъснена, докато при натискане на клавиши наистина се усеща, че реакцията е незабавна. Ще избера клавиатура, но тъй като същото може да се постигне и с клавиатурата, не се колебайте да използвате събитието, което ви харесва повече.

Сега, нека добавим слушателя на събитието и премахнете дневника на конзолата:

Тук трябва да забележите две важни неща. Първо, ние предоставихме името на събитието на клавиатурата на addEventListener и предоставяме параметъра на събитието на функцията за обратно извикване на обработчик на събития. Ще ни е необходим този параметър на събитието, за да проверим кой клавиш се натиска.

Така че, за да получим натиснатия ключ, можем да получим достъп до ключовото свойство на обекта на събитието:

Съвет: За да видите кои свойства на обекта на събитието са налице, просто направете console.log (събитие) и погледнете регистрирания обект. Знам, че това може да изглежда очевидно, но ако сте начинаещ, може да опитате и да потърсите в интернет за налични опции, което е добре, но отговорът обикновено е просто в дневника на конзолата.

След получаване на event.key, ние го преобразуваме в малки букви, защото би било добре тези клавишни комбинации да бъдат нечувствителни. Няма да е така всеки път, но този път е напълно добре.

Ако отворите конзолата на браузъра, щракнете обратно на страницата и опитайте да въведете нещо, можете да видите, че клавишът, който сте натиснали, се вписва в конзолата. Но ако сте внимателни, може да забележите, че той записва всеки натиснат ключ, което означава, че клавишите като Enter, Delete, Backspace и т.н. също се записват. Това е чудесно и може да бъде полезно, но в този случай искам да използвам само букви и цифри.

Ето защо, нека добавим проверка дали натиснатият клавиш е буква или цифра.

Създавайки променливата charList, която съдържа всички знаци, от които се интересуваме, ние можем да проверим дали ключът присъства в низа charList и ако не, просто се връщаме от функцията, без да влизаме в символа.

Сега, когато слушаме ключовите събития и сме в състояние да филтрираме само ключовете, които ни интересуват, е време да видим как можем да запишем последователност от въведени ключове.

Запазване на въведената последователност от ключове

За да запазим последователността на въведените ключове, трябва да добавим променлива, която ще съхранява тази последователност и която можем да актуализираме при всяко ключово събитие, което ни интересува.

Бихме могли да използваме низ, на който можем да обединим ключове, но също така можем да използваме масив, в който можем да съхраняваме отделни ключове като елементи от масива. В този прост случай на използване не е много важно какво използваме, но ще продължа с подхода на масива.

Подходът на масив е предложен подход за конкатенация на низове в случаите, когато много низове трябва да бъдат обединени, а също така ни позволява да използваме всички методи на масива, ако имаме нужда от тях.

Нека първо създадем променлива, наречена буфер, празен масив и натиснете новите клавиши в него.

Ако дефинираме буферния масив в слушателя на събитието за клавиатура, той ще се нулира всеки път, когато се натисне клавиш, и въведената последователност от ключове ще бъде загубена. Поради това ние трябва да го дефинираме извън слушателя на събитието на клавиатурата. Вече можем да запишем всички въведени ключове.

Но ако погледнем дневника, можем да забележим, че сега запазваме клавишите през цялото време и масивът просто нараства с натискане на нови клавиши. Проверете gif долу, за да видите резултата.

Непрекъснато добавяме натискания на клавиши в буферния масив

Вижте как след като напиша 'здравей' и известно забавяне, когато продължа да пишем, той все още има всички предишни стойности в масива. Това не е много полезно за това, което искаме. Добре би било да нулирате списъка с въведени ключове след изтичане на определено време от влизането на последния ключ.

За да изпълним условието, можем да използваме интервала от време между последните две натискания на клавиши. Ако този интервал е по-дълъг от определения период от време, тогава ще нулираме буферния масив. Нека да направим това по-нататък.

Ограничете интервала от време между натискането на клавишите

За това трябва да сравним кога се е случил последният и текущият натискане на клавиша и след това да проверим дали времето между тях е по-голямо от известно желано време закъснение, например 1 секунда, 0,5 секунди, каквото сметнете за най-удобно.

Така че, ние трябва да имаме поне една нова променлива, за да спестим последното време на натискане на клавиша. За да можем да проверим тази стойност, трябва да определим променливата извън слушателя на събитията и след това да я актуализираме след натискане на клавиша.

Представихме две нови променливи, lastKeyTime и currentTime. Можем да го направим само с lastKeyTime, но е много по-лесно да разсъждаваме с наличната променлива currentTime. Да видим какво направихме тук.

Първо дефинирахме променливата lastKeyTime и я инициализирахме с текуща стойност на времето. Трябваше да го инициализираме, защото имаме нужда от някаква стойност, за да извършим изчисление по-долу. Тук можем да използваме нула като стойност, но за да я направим последователна, нека използваме текущата стойност на времето.

След това дефинирахме променливата currentTime и я инициализирахме и с текущото време. В този момент някой може да попита защо две променливи, които имат еднаква стойност ?! Имайте предвид, че това са само стойности за инициализация, поне за lastKeyTime. Ще трябва да актуализираме тази стойност след всеки натискане на клавиш. И забележете, че lastKeyTime се инициализира извън слушателя на събитието, тоест само веднъж, докато currentTime ще се реинициализира при всяко натискане на клавиша.

Сега имаме важна част - проверката дали е минало достатъчно време между натисканията на клавишите. Ако има, искаме да нулираме буферната променлива, масивът, който съдържа символи, които потребителят е натиснал.

За да направите тази проверка, ние просто изваждаме lastKeyTime от променливата currentTime и проверяваме дали резултатът е по-голям от някакъв брой. В примера това число е 1000, тоест 1000 милисекунди или 1 секунда. Това е добра начална стойност и може да бъде променена по-късно, за да настроите по-добре изживяването.

Така че, ако условието currentTime - lastKeyTime> 1000 е вярно, ние нулираме буфера в празен масив: buffer = []; След това буферът ще получи нов ключ, но той ще бъде единственият в масива.

Последното нещо, което трябва да направим тук, е да актуализираме стойността lastKeyTime. Ние просто задаваме стойността му да бъде същата като currentTime стойност. При следващото натискане на клавиша lastKeyTime ще задържи времето, когато се е случило последното натискане на клавиша, докато currentTime ще получи нова стойност.

Актуализиране на фоновото изображение

И накрая, ние сме готови да направим това, което искахме, да променим фона въз основа на последователността на ключовете, които потребителят е въвел.

Ако потребителят въведе правилната клавишна последователност, трябва да вземем елемент на страницата и да актуализираме нейния фон в съответствие с въвеждането на потребителя. Изглежда, че трябва да имаме проверка за правилната последователност на ключовете.

Макар че това почти сигурно е вярно в повечето случаи, в този не е необходимо. Естеството на този проблем ни позволява да пропуснем проверката дали сме добре с това, което се случва, ако последователността на клавишите не е правилна.

Какво искам да кажа с това? Нека обясня подробно.

Ако искаме да променим фоновото изображение с помощта на CSS, ще трябва да актуализираме URL адреса за изображението. Ако изображенията са именувани като ключовите последователности, тогава всичко, което трябва да направим, е да прочетем входа, да го направим низ и да го зададем като URL за фоновото изображение.

Интересното е, че ако изображението не съществува, фонът няма да бъде показан. Което означава, че в случаите, когато имаме изображение, след което въведете нещо, което не съответства на съществуващ URL адрес на изображението, фоновото изображение ще изчезне.

В този случай аз съм добре с това, следователно проверката дали URL адресът е правилен не е необходима. Но ако бихме искали да запазим показаното в момента изображение, тогава тази проверка ще бъде необходима. Нека го направим по простия начин, тоест без тази проверка.

Премахнахме console.log и вместо това добавихме заявка, за да получим елемента, върху който ще се показва фона, и в следващия ред прилагаме фоновия URL адрес.

Тази структура на URL адреса ще зависи от вашата структура на проекта, в този случай изображенията са в папката 'images' и са в jpg формат. Важно да се отбележи тук е, че трябва да създадем низ от масива на буфера, така че да може да се използва в URL адреса на изображението. Правим това, като просто съединяваме елементите на масива с празен знак като присъединителен символ, buffer.join ('').

С това постигнахме основната цел на тази статия. Сега потребителят може да опита да въведе правилната последователност на клавишите и ако успее, фонът ще се промени. Това, което направихме, е просто и функционално и може лесно да бъде включено във всеки проект.

Но все пак е възможно да се направят някои подобрения. Така че, ако искате да имате по-голяма гъвкавост в кода, продължете да четете.

Подобряване на проекта

Разглеждането на кода ни показва, че използваме някои „глобални“ променливи. Глобални в смисъл, че те са в най-горния контекст на нашия скрипт, затворен във функцията за слушател на събития DOMContentLoaded, където ще бъде всички останали кодове, което означава, че те ще се смесват с други променливи в контекста на най-високо ниво на нашия скрипт.

Но декларацията на буфера на променливите и lastKeyTime не може да влезе в слушателя на събитието за клавиши, защото това ще ги нулира при всяко натискане на клавиша и ще наруши функционалността.

Опаковане на кода във функция

За да изолираме кода, който контролира нашите ключови събития, можем да поставим променливи и слушателя на събитията на клавиатурата в отделна функция.

С това функцията ни keyMapper е напълно независима от други кодове в нашия скрипт и може дори да бъде импортирана от друг файл, за да стане нещата още по-ясни. Но сега, когато имаме тази функция, можем да видим други възможности за подобрение.

Премахване на твърдо кодирани стойности

Едно от съществените неща, които можем да направим, за да направим функцията по-гъвкава е да премахнем всички твърдо кодирани стойности, които използваме, и вместо това да ги предадем на функцията като параметри.

Най-важните стойности, които са ни необходими, които са твърдо кодирани, са селекторът за фоновия елемент на контейнера (#background), частите за URL адреса на изображението и стойността за забавяне на времето в тази проверка, ако (currentTime - lastKeyTime> 1000). Има и променливата charList, но въпреки че можем да я предадем като параметър и да контролираме точно кои знаци са разрешени, можем да го оставим за сега, за да опростим нещата.

Вместо това това, което можем да направим с charList, е да го преместим от слушателя на събитията, във функцията keyMapper, за да не го декларираме при всеки натискане на клавиш. Така че, нека направим всички тези промени.

Сега функцията keyMapper приема четири параметъра, които ни позволяват да я персонализираме по-лесно. Сега, когато дадена функция има повече от три параметъра, тя започва да се чувства малко тромава, лесно е да забравите или смесите някои. Ще поправим това малко по-късно, но засега да продължим с това.

Създайте функция за обратно извикване

Желаното действие на нашия скрипт е да актуализираме фона на елемент на страницата. Това се случва в последния ред на сценария. Но може би искате да се случи нещо съвсем различно, след като конкретна клавишна последователност бъде въведена от потребителя, може би да пусне звук, да отвори диалогов прозорец или каквото се сетите.

В настоящата ни версия на скрипта можем да актуализираме само фона. Но би било наистина хубаво, ако можем лесно да кажем нашия скрипт, ако вместо това искаме да направим нещо друго, тоест да използваме функцията keyMapper, за да можем да изпълним определена функция, която сме дефинирали. За да направим това, можем да определим функция, която можем да предадем на функцията keyMapper и след това да я изпълним.

Както можете да видите, новата функция keyMapper приема още един параметър (dooh!), Обратно извикване, което е функция, която искаме да изпълним след натискане на клавиш. На последния ред вътре в keyMapper функцията, ние извикваме тази функция за обратно извикване и предаваме на нея някои променливи, от които се нуждае. Когато се извиква keyMapper, ние предаваме на него функцията, наречена updateBackground, която се декларира след функцията keyMapper.

Функцията updateBackground основно съдържа тези два последни реда от keyMapper, които сме премахнали. С това напълно сме отделили запазването на последователността на ключовете и действието, което се извършва след получаването на тази последователност.

Ако трябваше да напишем нова функция и да я предадем на keyMapper като обратен сигнал, вместо функцията updateBackground, можем да получим съвсем различно поведение.

Стъпка назад

Едно нещо, което започна да ме бъга много, е фактът, че сега имаме пет параметъра за функцията keyMapper и четири за функцията updateBackground, а всички параметри за updateBackground също се предават на функцията keyMapper, преди да бъдат предадени на функция updateBackground.

Сега, след като разделихме функцията за обратно извикване от функцията keyMapper, има смисъл просто да върнем твърд код обратно необходимите стойности във функцията updateBackground, тъй като по този начин ще съдържаме цялата необходима информация във функцията за обратно извикване и можем да я извикаме само с един параметър , ключовата последователност, която keyMapper предоставя.

Това също означава, че функцията keyMapper може да бъде извикана само с два параметъра, keystrokeDelay и функцията за обратно извикване. Ето актуализирания скрипт:

Това изглежда малко по-чисто и функционалността и параметрите са добре разделени.

Все пак можем да се справим малко по-добре от това.

Може би сте забелязали, че във функцията keyMapper има друг низ, твърдо кодиран, името на събитието на клавиша. Ако вместо това решихме да използваме keyMapper за събитие за клавиатура, ще трябва да го променим директно във функцията. Това не е непременно голям проблем, но би било хубаво, ако можем да контролираме и тези параметри.

И така, добавяме трети параметър към функцията keyMapper?

Бихме могли да направим това, но нека да направим крачка напред и да го направим само един параметър. Нека представим ...

Опциите обект

Наистина приятен начин за предоставяне на параметри за функции е предоставянето на обекта със свойствата, които държат стойностите на параметрите. Той е най-полезен в случаите, когато има много параметри, които трябва да бъдат предадени на функцията и особено в случаите, когато тези параметри са незадължителни и представляват някаква конфигурация за функцията.

Параметрите, от които се нуждаем за функцията keyMapper, keystrokeDelay и новия eventType, които трябва да бъдат добавени, наистина изглеждат като стойностите на конфигурацията. Едно допълнително предимство от използването на обекта за предаване на параметрите на функцията е, че редът на параметрите няма значение.

Ето как можем да направим това сега:

Обектът за опции сега съдържа името на събитието, което трябва да се слуша и закъснението между натисканията на клавишите. Вътре в функцията keyMapper имаме и тези два реда:

const eventType = опции && options.eventType || "Keydown";
const keystrokeDelay = опции && options.keystrokeDelay || 1000;

Това е мястото, където направихме параметрите си по избор. Ако стойностите са предоставени чрез обекта на опциите, те ще бъдат използвани, ако не, ще използваме стойностите по подразбиране. Можете да тествате това, като промените стойностите в обекта с опции. Можете напълно да пропуснете обекта на опции във функцията за повикване до keyMapper и той все още ще работи със стойностите по подразбиране.

Можем също да предадем функцията за обратно извикване през обекта с опции, но тъй като без тази функция не бихме имали разумна функционалност, изглежда по-добре да я предадем като отделен и задължителен параметър.

Друго, което трябва да се забележи при тази последна промяна, е, че редът на параметрите за функцията keyMapper е обърнат. Не беше необходимо да го правите, но ако параметърът е незадължителен, той обикновено се предоставя след необходимите, в противен случай, ако искате да пропуснете параметъра на опционалните опции, ще трябва да извикате функция keyMapper така:

keyMapper (null, updateBackground); // null е стойността за обекта с опции

Добавете управлението на държавата

Не, това няма да бъде редуцирано или друга библиотека за управление на държавата, би било глупаво да добавите цялата библиотека за такъв малък скрипт. Ние просто ще организираме нашите променливи, които държат състоянието на скрипта, буфера и lastKeyTime, в един обект и ще го актуализираме при всяка промяна.

Сега последователността на ключа и времето на последното натискане на клавиша се запазват като свойство на буфера и свойството lastKeyTime на обекта състояние. При всяко натискане на клавиш копираме свойството на буфера в локалната буферна променлива и актуализираме съответно тази променлива.

След това състоянието се актуализира с локалната буферна променлива и променливата currentTime. В края се извиква обратно повикване с текущата стойност на буфера.

Едно нещо, което трябва да се отбележи тук, е, че ние никога не актуализираме директно съществуващия обект на състояние, нито неговото свойство на буфер, а по-скоро винаги създаваме нов обект за състоянието и нов масив за буфера. Това е известно като неизменна актуализация на състоянието и е предпочитаният начин за актуализиране на състоянието. Ние никога не променяме предишната стойност / обект, винаги присвояваме новата стойност / обект на държавата.

Редът: буфер = [ключ]; е нулирането на буфера в случай, че е минало повече време между натисканията на клавишите, отколкото дефинирането на keystrokeDelay.

Линията буфер = [… state.buffer, ключ]; използва оператора за разпространение ... за да запълни новия масив със стойностите от буфера на състоянието и след това добавяме текущия ключ към масива. С това буферът се актуализира.

И накрая, състоянието на реда = {буфер: буфер, lastKeyTime: currentTime}; актуализира състоянието чрез присвояване на новия обект с нови стойности на буфер и lastKeyTime.

В тази последна проба от код премахнах теста за характер, тъй като той всъщност не подобрява функционалността на скрипта, но разбира се, винаги можете да добавите своя собствена проверка, за да контролирате как се държи скриптът.

Последен щрих

Нашият сценарий е завършен сега. Можем да зададем опции, да създадем всяка функция за обратно извикване, която искаме, състоянието се актуализира неизменно. Като цяло нашата функция KeyMapper вече е доста гъвкава.

Ако разгледате по-отблизо страницата в браузъра, ще забележите, че вляво има тази колона с малко информация. В долната част на колоната можете да видите местата за ключови последователности („някои клавиши“), които потребителят е въвел, и за съобщението за обратна връзка („все още нищо“) за потребителя. Те в момента са безполезни, но можем да променим това лесно. Има няколко начина за това.

Най-простият начин е да добавите повече функционалност към функцията updateBackground за обратно извикване. Но това ще промени това, което прави функцията и името й няма да описва правилно какво прави.

Така че, можем да запишем новия си код вътре в друга функция и да извикаме тази функция от функцията updateBackground. Това не е идеално решение, но е по-добре, отколкото просто да зашлевите новия код, който няма нищо общо с актуализирането на фона във функцията updateBackground. Ето как бихме направили това:

Друг начин да направите това е просто да извикате keyMapper втори път с функцията updateUI като функция за обратно извикване:

Това е малко по-добре, когато става въпрос за разделянето на функционалността, но сега имаме двама слушатели на събития, които по принцип правят едно и също нещо.

Би било добре, ако можем да имаме само един слушател на събития за ключовите събития и след това да извикаме всички функции, които искаме, когато събитието се случи.

И можем да направим това, трябва само да предоставим всички тези функции на функцията keyMapper и най-добрият начин да направим това е да предадем масив, който да съдържа препратките към всички функции за обратно извикване, които искаме да изпълним, когато се случи ключово събитие. Актуализирането на кода за постигане на това е наистина просто:

И така, функцията keyMapper получава масив от референции за функции:

keyMapper ([updateBackground, updateUI], опции);

и след това при всяко обновяване на буфера всички онези функции се извикват:

callbackList.forEach (callback => callback (буфер));

Добавянето на нова функционалност за ключови събития вече е толкова лесно, колкото да напишем функция, която да преформира това, което искаме и да предаде своята препратка към функцията keyMapper, когато я извикаме.

Актуализиране (21 февруари 2019 г.)

В коментарите беше казано, че потребителят може да влезе в път като ../myimage и това ще получи достъп до някакъв файл, който е на едно ниво в структурата на директория ... ако съществува. За да избегнете възможността да използвате каквито и да е символи, различни от букви и цифри, тези редове се добавят към функцията updateBackground:

const validKeys = keySequence.every (key =>! isNaN (parseInt (key)) || key.toLowerCase ()! == key.toUpperCase ());
ако (! validKeys) се върнат;

Също така, функцията keyMapper връща ключове, които не са преобразувани в малки букви. Тази конверсия сега се случва във функциите за обратно извикване, които трябва да направят това, като updateUIfunction.

Актуализиране (15 февруари 2019 г.) - JavaScript казва, че това е вярно (0 == невярно) и това ('' == невярно)

След като бях напомнен в коментарите за опасностите от използването на оценката на стойности на триети в JavaScript, реших да се справя с това и да коригирам проблемната част.

Проблемът е в тези редове:

const eventType = опции && options.eventType || "Keydown";
const keystrokeDelay = опции && options.keystrokeDelay || 1000;

Както вече споменах, това ще използва стойността от обекта с опции, ако съществува, и ако не съществува, ще използва стойността по подразбиране. С изключение на това, че няма да го направи във всички случаи. Ако по някаква причина options.eventType и options.keystrokeDelay имат стойности, които се оценяват на фалшиви, тогава тези стойности няма да се използват и ще прескачат до по подразбиране.

Очаквахме да не бъде определена стойността за тези свойства, ако те не съществуват, което ще оцени като невярно и след това ще използваме по подразбиране. Стойността null също може да оцени на false, което също е добре.

Това, което не разгледахме тук, е видът на променливите. Това е така, защото в зависимост от типа на променливата, можем да имаме други фалшиви стойности.

Очакваме първото свойство, options.eventType, да бъде низ. Но какво ще стане, ако това е празен низ? JavaScript казва, че празният низ трябва да се оценява на false в такива сравнения. Което означава, че ако прехвърлим празен низ до свойството options.eventType, все пак ще получим събитието по подразбиране по подразбиране, тъй като празният низ ще оцени на невярно. Но ако не преминем нищо, няма да се получи! Така че, в този случай той действително работи в наша полза да се провали тази проверка за emtpy низ и да се използва типът събитие по подразбиране по подразбиране. Това е добре.

Очаква се второто свойство, options.keystrokeDelay, да бъде число. А в случая на числата, JavaScript казва, че нулата трябва да се оцени като невярна. Ако искахме да преминем нулата за стойността на закъснението, тя няма да бъде приета и тя ще се върне до стандартната стойност от 1000ms. Но от друга страна, чувам ви да кажете „Кой би могъл да напише толкова бързо, че нулевата стойност изобщо трябва да се има предвид?“. И вие сте прави, в този случай всичко това е безсмислено, можехме да слушаме само един ключ наведнъж.

Значи и тук сме добре, няма нужда да правим нищо? Всъщност не. Ами ако предоставеният номер е отрицателен? Със сигурност няма да се върне назад във времето и ще реагира на натискане на клавиши, преди да се появят, но това ще стане веднага щом се появят. По принцип тя е същата като задаване на стойността на нула, но тя преминава теста за надеждност. Сега виждаме, че тук имаме нужда от друго условие, имаме нужда от числа, по-големи от нула. Сега решихме проблема, но какво ще стане, ако някой премине число като 10? Достатъчни ли са 10 мс, за да свържете два натискания на клавиш за вас? Не за мен, това е сигурно. Едва ли мога да го направя със закъснение от 250ms. От моя гледна точка като разработчик на това приложение не виждам причина да разреша закъснения по-малки от минималните, които ми трябват, за да свържа два натискания на клавиши. Затова реших да огранича закъснението до минимум 300 милисекунди.

Ето актуализиран код за функцията keyMapper:

Добавих функция hasProperty, която ще провери дали свойството съществува на обекта и само след като премине тази проверка, можем да преминем към следващите проверки. На линията за забавяне можете също да видите options.keystrokeDelay> = 300 проверка, която ще ограничи забавянето до 300ms.

Едно последно нещо, което бих искал да спомена е, че тези проблеми, за които говоря в този актуализиран раздел, могат да бъдат избегнати на първо място, ако напишем правилни тестове за нашия код. Например, лесно можем да мислим за проблемите с празния низ, когато обмисляме какви възможни входове би могла да получи функцията за свойството eventType. Същото се отнася и за свойството keystrokeDelay, докато разглеждаме възможните входове, можем бързо да осъзнаем, че използването на нула, отрицания или дори твърде малки числа не работи добре.

заключение

Въпреки че основната функционалност на реагирането на конкретна последователност от ключове беше кратка и проста задача, исках да отида няколко стъпки по-нататък и да покажа как можем наистина да направим гъвкава функция, която да ни даде възможност за малко повече контрол. Постигнахме това с предаването на списъка с функции за обратно извикване и обект на опции към функцията keyMapper.

Но можете да направите функцията още по-персонализирана, като предоставите повече опции и добавите кода във функцията, свързана с тези нови опции.

В зависимост от това, от което се нуждаете, можете да го удължите колкото искате. Разбира се, бъдете разумни в това, защото искате кодът ви да бъде четим и лесно да разсъждавате.

Надявам се тази статия да ви даде някои идеи как можете да използвате това във вашите проекти и, моля, не се колебайте да ги споделите в коментарите, ако желаете.

Благодаря ви за четенето и Честита Нова година!