'How to add konami code in a website based on html?
I was asked to implement the Konami Code in a website I'm currently working on. It should do the following:
Change Background Image
Play sound
Bring some pop-up
What's the easiest way to achieve this using javascript?
Solution 1:[1]
compact version:
function onKonamiCode(cb) {
var input = '';
var key = '38384040373937396665';
document.addEventListener('keydown', function (e) {
input += ("" + e.keyCode);
if (input === key) {
return cb();
}
if (!key.indexOf(input)) return;
input = ("" + e.keyCode);
});
}
onKonamiCode(function () {alert('\o/')})
Solution 2:[2]
My own compact and cleaned version inspired by the answers here:
let cursor = 0;
const KONAMI_CODE = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
document.addEventListener('keydown', (e) => {
cursor = (e.keyCode == KONAMI_CODE[cursor]) ? cursor + 1 : 0;
if (cursor == KONAMI_CODE.length) activate();
});
In this case, the activate() function is called when triggered.
Solution 3:[3]
Silentdrummer has a good answer. I'm not entirely sure, but I think it could end up taking up too much memory on typing intensive pages. It's good practice to reset. Either way, here's an alternative.
// Cheat Codes
neededkeys = [38,38,40,40,37,39,37,39,66,65], started = false, count = 0;
$(document).keydown(function(e) {
key = e.keyCode;
if (!started) {
if (key == 38) {
started = true;
}
}
if (started) {
if (neededkeys[count] == key) {
count++;
} else {
reset();
}
if (count == 10) {
reset();
// Do your stuff here
alert('Cheat Codes Activated');
$('body').css('background-color', '#FFA8A8');
// Turn down for what
var s=document.createElement('script');
s.setAttribute('src','https://nthitz.github.io/turndownforwhatjs/tdfw.js');
document.body.appendChild(s);
}
} else {
reset();
}
});
function reset() {
started = false;
count = 0;
}
Solution 4:[4]
as a typescript module
const Konami = (() => {
// up, up, down, down, left, right, left, right, b, a, enter
const SEQUENCE: Array<number> = [
38,
38,
40,
40,
37,
39,
37,
39,
66,
65,
13,
];
let head: number = 0;
let isActive: boolean = false;
let callback: Function | undefined;
const start = (cb: Function): void => {
if (isActive) {
return;
}
window.addEventListener("keydown", onKeyDown);
callback = cb;
isActive = true;
};
const stop = (): void => {
if (!isActive) {
return;
}
isActive = false;
window.removeEventListener("keydown", onKeyDown);
};
const onKeyDown = (event) => {
if (event.keyCode === SEQUENCE[head]) {
head++;
if (head === SEQUENCE.length) {
if (callback instanceof Function) {
callback();
}
head = 0;
}
} else {
head = 0;
}
};
return {
start,
stop,
};
})();
export default Konami;
implementation:
Konami.start(() => { alert("konami sequence entered!"); });
notes: SEQUENCE is an array of the expected inputs. by using the head var, the order checking and number of correct inputs is maintained. it also provides a simple way to restart if input deviates from the sequence. it also eliminates the needs for a "count" var.
Solution 5:[5]
This is a solution I came up with around 3 or 4 years ago. In my case I chose keyUp to keep it separate from any actions that occur from keyDown events. Also there is no need to specify what keys are allowable since the for loop checks which key was released against all the keys on the keyboard.
var konamicode = [38,38,40,40,37,39,37,39,66,65];
var kc=0;
function checker() {
if (kc==10) {
// What you want to occur when code matches goes in here.
kc=0; // This resets the sequence.
alert("It Worked!");
}
}
function keyUp(e) {
var keynum;
if (window.event) { keynum = event.keyCode; }
else if (e.which) { keynum = e.which; }
for (i = 0; i < 222; i++) { // The 222 represents all the keys on the keyboard.
var kx=konamicode[kc]; // kx represents the current position in the code sequence.
if (keynum == i) {
// Checks to see if key matches sequence, and resets sequence if it doesn't.
if (i!=kx){kc=0;} else {kc++;}
}
}
checker();
}
Solution 6:[6]
I really liked Peter's answer, so I made it namespaced and made the callback optional. I also used jquery because I like it ¯\_(?)_/¯
var Konami = Konami || {};
Konami.key = '38384040373937396665';
Konami.onCode = function(callback) {
var input = '';
$(document).on("keydown", function(e) {
input += ("" + e.keyCode);
if (input === Konami.key) {
if(typeof callback == 'undefined') {
return alert("??????????");
}
else {
return callback();
}
}
if (!Konami.key.indexOf(input)) return;
input = ("" + e.keyCode);
});
}
Konami.offCode = function() {
$(document).off("keydown");
}
Konami.onCode();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Solution 7:[7]
To create your own "Konami Code" add the following Code snippet in your HTML Code. PS: Change the const secretCode to ... what ever you want :). With the current code you have to type 'arrow up' button, then 'h' then 'i' and last but not least the 'arrow down' button.
Questions? Please ask.
<script>
const pressed = [];
const secretCode = 'ArrowUphiArrowDown';
window.addEventListener('keyup', (e) => {
console.log(e.key);
pressed.push(e.key);
pressed.splice(-secretCode.length - 1, pressed.length - secretCode.length);
if(pressed.join('').includes(secretCode)) {
console.log("Any source code that will be executed if you enter the correct code.");
}
console.log(pressed);
})
</script>
Solution 8:[8]
Piggybacking off Ehsan Kia,
I haven't seen anyone handling cases where the up key could be pressed 3+ times, and technically the code would have been input correctly.
Minified it a bit because the conditionals got long.
let c = 0;
const kCode = [38,38,40,40,37,39,37,39,66,65];
document.addEventListener('keydown', (e) => {
c = (e.keyCode == kCode[c] ? c + 1 : (e.keyCode-38 ? 0 : (c ? (kCode[c-1] == 38 ? c : 0) : 0)));
if(c == kCode.length) activate();
});
Solution 9:[9]
Use the following code.
const keySequence = [];
let konamiString = '';
const konamiCode = [
'ArrowUp',
'ArrowUp',
'ArrowDown',
'ArrowDown',
'ArrowLeft',
'ArrowRight',
'ArrowLeft',
'ArrowRight',
'b',
'a'
];
document.addEventListener('keydown', function(e) {
// To make sure it freezes the scroll when
// the first two keypresses are "ArrowUp"
if (keySequence[0] === 'ArrowUp' && keySequence[1] === 'ArrowUp' && e.key === 'ArrowDown') {
e.preventDefault();
}
});
document.addEventListener('keyup', function(e) {
const doc = document.documentElement;
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
// This make sure it only work
// when the window `scrollTop` is 0.
if (top === 0) {
keySequence.push(e.key);
keySequence.splice(-konamiCode.length - 1, keySequence.length - konamiCode.length);
konamiString = konamiCode.join('');
if (keySequence.join('').includes(konamiString)) {
// Trigger your easter egg here
}
}
});
The code also checks for the scrollTop of the window so that it won't scroll down when the first two keypresses are "ArrowUp" and the scrollTop of the window is 0
I'm already using this code on my blog without any hiccup.
Solution 10:[10]
this is my answer with using JQuery :
var konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13];
var emptyArray = [];
$(document).keyup (function (e){
emptyArray.push(e.keyCode);
if (JSON.stringify(konamiCode) === JSON.stringify(emptyArray)) {
alert('there you go')
}
});
Solution 11:[11]
As mentioned in comments, the currently accepted answer does not work if user presses "up" 3 times instead of twice.
If we think about this problem, we should realize that the flaw in the approach is assuming a single "input sequence". What I mean by this is that every time the user presses the first key of the cheat code, we should consider that this might be a new input sequence.
Reflecting on this flaw, a simple solution is to keep track of multiple input sequences, not just a single input sequence. As such, all of the answers here that use a single integer inputPosition (or whatever it might be called) are going to be flawed*.
*To be clear, a solution could use a single integer inputPosition if it did some "reverse walk" check when a sequence is broken to see if a part of it could be the beginning of a new sequence, but that would be difficult to write and unpleasant to read.
So,here is my version:
const keyCodesByLabel = {
left: 37,
up: 38,
right: 39,
down: 40,
a: 65,
b: 66
};
const konamiCode = [
'up',
'up',
'down',
'down',
'left',
'right',
'left',
'right',
'b',
'a'
];
const konamiKeyCodes = konamiCode.map(label => keyCodesByLabel[label]);
const activateCheats = () => {
alert("godmode enabled");
};
var inputPositions = [];
const incrementOrRemove = (inputPositions, keyCode) => {
return inputPositions.reduce((acc, inputPosition, i, arr) => {
if (keyCode == konamiKeyCodes[inputPosition]) {
inputPosition++;
if (inputPosition == konamiCode.length) {
inputPositions = [];
activateCheats();
arr.splice(1); // eject early by mutating iterated copy
return [];
} else {
acc.push(inputPosition);
return acc;
}
} else {
return acc;
}
}, []);
};
const handleKeyCode = keyCode => {
if (keyCode == konamiKeyCodes[0]) {
inputPositions.push(0);
}
if (inputPositions.length > 0) {
inputPositions = incrementOrRemove(inputPositions, keyCode);
}
};
document.addEventListener('keydown', ({ keyCode }) =>
handleKeyCode(keyCode)
);
inputPositions starts as an empty array.
When a key is pressed:
- if it is the "first cheat code key" then a new element
0is added to the array. - if the
inputPositionsarray is not empty, the array is "reduced" to another array, whereby we either:
- discard an element whose sequence has been broken (the next required key is not the same as what was pressed) or
- increment an element whose sequence has been continued (the next required key is the same as what was pressed).
During the reduce, if an element equals the length of the cheat code, we know the cheat code has been successfully entered. Reset inputPositions to empty array, exit the reduce and call activateCheats or whatever you want to happen.
Note that this solution is more powerful than just handling the "up up up" case.
For example, imagine the cheat code is "a a b a a c". If user enters "a a b a a b", they should (rightly) expect to be able to enter "a a c" and activate cheats. But any solution that resets the "position" when sequence breaks will not catch it. More importantly, any solution that checks if pressed key is the initial key will not catch it. The only holistic solution is to have an array of input positions.
Solution 12:[12]
Here's a simple version which accepts a callback function and optionally a list of key codes, defaulting to the konami code.
We demonstrate by showing the key codes as typed and reporting when a konami code is hit. We also run another one looking for the keys "K", "O", "N", "A", "M", and "I". (We inefficiently keep looking for our output element. This is just for demonstration, so I don't think it matters.)
const onKonami = (action, seq = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]) => {
const target = seq .join (',')
let current = []
window.addEventListener ('keydown', (e) => {
current = current .concat (e.keyCode) .slice (-seq.length)
if (current .join (',') == target) {action (current)}
})
}
onKonami (
(seq) => {document .getElementById ('output') .innerHTML += `\nKonami code found: [${seq}]\n`}
)
onKonami (
(seq) => {document .getElementById ('output') .innerHTML += `\nThe word Konami code found: [${seq}]\n`},
[...'KONAMI'].map(c => c .charCodeAt (0))
)
<p>Click here and then type</p><p>Try the Konami code (up, up, down, down, left, right, left, right, b, a)</p><p><b>Codes:</b></p><pre id="output"></pre>
<script>document.addEventListener('keydown', e => document .getElementById('output') .innerHTML += e.keyCode + ' - ')</script>
We keep a current array of the last n keystrokes, where n is the length of our target sequence. If that sequence matches our target, we fire the callback.
It would not be difficult to pass also an event target, so we only listen when a particular element has focus. I leave that as an exercise.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
