How to toggle the content of an element by pressing a key? - javascript

Im currently trying to make a website where if i press q the p tag will change from "Q" to "A". This currently works with the code below. However the problem is that when pressing q it needs to go back to "A". I've tried making it work with removeEventListeners but it doesn't seem to work.
<script>
document.addEventListener("keypress", event => {
if (event.key == "q") {
document.getElementById("P1").innerText = "Q"
}
});
</script>

Like #diegod suggested in the comments you should check which character is currently shown in the p element and change the innerText accordingly:
const p1 = document.getElementById("P1");
document.addEventListener("keypress", event => {
if (event.key == "q") {
if (p1.innerText == "A") {
p1.innerText = "Q";
} else {
p1.innerText = "A";
}
}
});
Using ternary operator example:
const p1 = document.getElementById("P1");
document.addEventListener("keypress", event => {
if (event.key == "q") {
p1.innerText = p1.innerText == "A" ? "Q" : "A";
}
});

const $ = (prop) => document.querySelector(prop)
const changeToQ = () => {
$('p').innerText = 'Q'
}
const reset = () => {
$('p').innerText = 'A'
}
document.addEventListener('keydown', (e) => {
if (e.key === 'q') changeToQ()
})
document.addEventListener('keyup', (e) => {
if (e.key === 'q') reset()
})
I think this is what are you looking for

have your tried to make else statement
else{ document.getElementById("P1").innerText = "A"}

Related

Building a simple OPT forum

So I am trying to building a simple OPT forum which I have build but now I want to add reverse functionality for example when user press backspace input should focus on previous element.
const inputs = document.querySelectorAll('.input input')
inputs.forEach((e, i) => {
e.dataset.index = i;
e.addEventListener("input", () => {
if (e.value.length >= 1) {
if (e.dataset.index >= i && inputs.length - 1 > i) {
e.nextElementSibling.focus();
}
} else {
if (inputs.length - 1 > i || e.dataset.index < inputs.length) {
e.previousElementSibling.focus();
}
}
})
})
You would have to detect the press of the backspace key.
It looks like you want each input to take exactly one character. In that case I would suggest that you keep the current character selected, and to make arrow keys also move the focus.
Don't name the input element e, as e is usually used as name for the event object -- just to avoid confusion for someone reading your code.
Here is one way to do it:
const inputs = [...document.querySelectorAll('.input input')];
inputs.forEach((input, i) => {
const previous = () => inputs[i-1]?.focus?.();
const next = () => inputs[i+1]?.focus?.();
input.addEventListener("input", () => {
if (input.value.length > 1) input.value = input.value[0];
if (input.value.length) next();
});
input.addEventListener("keydown", (e) => {
if (e.key == "ArrowRight") next();
if (e.key == "ArrowLeft") previous();
});
input.addEventListener("keyup", (e) => {
if (e.key == "Backspace") {
previous();
e.preventDefault();
}
});
});
inputs[0].focus();
// Ensure that current input has its content selected
document.addEventListener("selectionchange", () => {
const input = document.activeElement;
if (inputs.includes(input)) input.setSelectionRange(0, 1);
});
.input input { width: 1em }
<div class="input">
<input><input><input><input><input>
</div>

Different way to know the origin of the click, optimizing my tab system

I'm learning JavaScript, and I've done this code, I just want to be sure that I'm on the right way, would you have done it differently?
What bothers me is especially to have passed parameters to the functions "next" and "updateArrows" to know the origin of the clicks
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) {
arrow.addEventListener("click", function () {
updateArrows(arrow);
});
}
for (let step of changeStep) {
step.addEventListener("click", function () {
next(step);
});
}
function updateArrows(arrow, currentStep = null, update = true) {
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(step, update = true) {
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
I see what you mean.
Yes you are right. You can do it better...
Instead of passing the parameter arrow you can read from an event object
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) arrow.addEventListener("click", updateArrows);
for (let step of changeStep) step.addEventListener("click", next);
function updateArrows(e, currentStep = null, update = true) {
let arrow = null
e.target ? arrow=e.target : arrow=e
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(e, update = true) {
let step = null
e.target ? step = e.target : step=e
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
This should work, if not please contact me beforehand.
While activating the eventListener an event object is passed to function and e.target is an element which was clicked in this case.
What I did was crucial because you sometimes call this function from an eventListener and sometimes from a code. If the element has e.target then it's from an eventListener and if not then it's from code.
Didn't have a chance to test it since I don't have the rest of the code. Let me know if it works.

Custom select (dropdown) don't focusing on first element when active

I'm making a custom dropdown and want to make good keyboard support. How do I get focus on my first LI when the UL visible. Didn't find answer by my self.
I take code from there https://codepen.io/beforesemicolon/pen/abNpjKo
On CodePen focus on first element works. But on my page - no... please, tell me if you know :)
Here is the code:
function DropDown(dropDown) {
const [toggler, menu] = dropDown.children;
const handleClickOut = e => {
if(!dropDown) {
return document.removeEventListener('click', handleClickOut);
}
if(!dropDown.contains(e.target)) {
this.toggle(false);
}
};
const setValue = (item) => {
const val = item.textContent;
toggler.textContent = val;
this.value = val;
this.toggle(false);
dropDown.dispatchEvent(new Event('change'));
toggler.focus();
}
const handleItemKeyDown = (e) => {
e.preventDefault();
if(e.keyCode === 38 && e.target.previousElementSibling) { // up
e.target.setAttribute("aria-selected", "false");
e.target.previousElementSibling.setAttribute("aria-selected", "true");
e.target.previousElementSibling.focus();
} else if(e.keyCode === 40 && e.target.nextElementSibling) { // down
e.target.setAttribute("aria-selected", "false");
e.target.nextElementSibling.setAttribute("aria-selected", "true");
e.target.nextElementSibling.focus();
} else if(e.keyCode === 27) { // escape key
this.toggle(false);
} else if(e.keyCode === 13 || e.keyCode === 32) { // enter or spacebar key
setValue(e.target);
}
}
const handleToggleKeyPress = (e) => {
e.preventDefault();
if(e.keyCode === 27) { // escape key
this.toggle(false);
} else if (e.keyCode === 13 || e.keyCode === 32) { // enter or spacebar key
this.toggle(true);
} else if (e.shiftKey && e.keyCode === 9) { // tab + shift key
this.toggle(false);
document.getElementById("message_email").focus();
} else if (e.keyCode === 9 ) { // tab key
this.toggle(false);
document.getElementById("message_text").focus();
}
}
toggler.addEventListener('keydown', handleToggleKeyPress);
toggler.addEventListener('click', () => this.toggle());
[...menu.children].forEach(item => {
item.addEventListener('keydown', handleItemKeyDown);
item.addEventListener('click', () => setValue(item));
});
this.element = dropDown;
this.value = toggler.textContent;
this.toggle = (expand = null) => {
expand = expand === null ? menu.getAttribute("aria-expanded") !== "true" : expand;
menu.setAttribute("aria-expanded", expand);
if(expand) {
menu.children[0].focus();
toggler.classList.add('active');
menu.children[0].focus();
document.addEventListener('click', handleClickOut);
dropDown.dispatchEvent(new Event('opened'));
//toggler.blur();
} else {
toggler.classList.remove('active');
toggler.focus();
dropDown.dispatchEvent(new Event('closed'));
document.removeEventListener('click', handleClickOut);
}
}
}
const dropDown = new DropDown(document.querySelector('.message__dropdown'));
Heh... problem was in CSS. Property {transition-duration} was written by me for UL (I mean for changing background color, but I didn't choose {transition-property}).
After I remove {transition-duration} focusing is working well. Oh my God... it tooks fore hours

How to use event.code and event.button in if, else condition properly?

my goal is to build playing drums using vanilla Javascript, whole code worked perfectly when I was using only the keydown event, unfortunately, my drums have to play also on mouse 'click'.
When I added "|| event.button == aKey == 0" this part to my condition, it still plays, but only one sound on each key. So I assumed that there is something wrong with my condition.
And also if it would be possible to somehow make my code less repeatable, I would appreciate it.
const aKey = document.getElementById("A-key");
const sKey = document.getElementById("S-key");
const dKey = document.getElementById("D-key");
const fKey = document.getElementById("F-key");
const gKey = document.getElementById("G-key");
const hKey = document.getElementById("H-key");
const jKey = document.getElementById("J-key");
const kKey = document.getElementById("K-key");
const lKey = document.getElementById("L-key");
const playFunction = (event) => {
if (event.code == "KeyA" || event.button == aKey == 0) {
//
let audioA = document.createElement("AUDIO");
if (audioA.canPlayType("audio/wav")) {
audioA.setAttribute("src","sounds/boom.wav");
}
audioA.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioA);
} else if (event.code == "KeyS" || event.button == sKey == 0 ) {
let audioS= document.createElement("AUDIO");
if (audioS.canPlayType("audio/wav")) {
audioS.setAttribute("src","sounds/clap.wav");
}
audioS.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioS);
} else if (event.code == "KeyD" || event.button == dKey == 0) {
let audioD = document.createElement("AUDIO");
if (audioD.canPlayType("audio/wav")) {
audioD.setAttribute("src","sounds/hihat.wav");
}
audioD.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioD);
} else if (event.code == "KeyF" || event.button == fKey == 0) {
let audioF = document.createElement("AUDIO");
if (audioF.canPlayType("audio/wav")) {
audioF.setAttribute("src","sounds/kick.wav");
}
audioF.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioF);
} else if (event.code == "KeyG" || event.button == gKey == 0) {
let audioG = document.createElement("AUDIO");
if (audioG.canPlayType("audio/wav")) {
audioG.setAttribute("src","sounds/openhat.wav");
}
audioG.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioG);
} else if (event.code == "KeyH" || event.button == hKey == 0) {
let audioH = document.createElement("AUDIO");
if (audioH.canPlayType("audio/wav")) {
audioH.setAttribute("src","sounds/ride.wav");
}
audioH.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioH);
} else if (event.code == "KeyJ" || event.button == jKey == 0) {
let audioJ = document.createElement("AUDIO");
if (audioJ.canPlayType("audio/wav")) {
audioJ.setAttribute("src","sounds/snare.wav");
}
audioJ.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioJ);
} else if (event.code == "KeyK" || event.button == kKey == 0) {
let audioK = document.createElement("AUDIO");
if (audioK.canPlayType("audio/wav")) {
audioK.setAttribute("src","sounds/tink.wav");
}
audioK.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioK);
} else if (event.code == "KeyL" || event.button == lKey == 0) {
let audioL = document.createElement("AUDIO");
if (audioL.canPlayType("audio/wav")) {
audioL.setAttribute("src","sounds/tom.wav");
}
audioL.setAttribute("autoplay", "autoplay");
document.body.appendChild(audioL);
} else {
console.log("Try to use specified keys.")
}
};
window.addEventListener('keydown', playFunction, false);
window.addEventListener('click', playFunction, false);
event.button == kKey == 0 doesn't make much sense. This evaluates like (foo == bar) == baz, that is, the first condition is tested, then the result of that (true or false) is checked against the other condition. If you want to test multiple conditions, use || or && between every condition. Also, always use === to avoid surprising conversion behavior.
As for the refactor request, whenever you have a bunch of branches that only differ based on parameterizable values, like the button text or the sound, use a data structure such as an array or object and index/key into it.
Specifically, all you need here are key -> audio or audio URL pairs, so an object seems suitable.
Once you have such a structure, then you can access the power of loops to DRY out your code.
I don't have your audio files or markup, so you can try to adapt this proof of concept to your project:
const baseURL = `https://upload.wikimedia.org/wikipedia/commons/`;
const sounds = {
a: `7/7c/Bombo_Leg%C3%BCero_Grave.ogg`,
s: `7/7f/Bombo_Ac%C3%BAstico.ogg`,
d: `3/35/Bongo_Agudo.ogg`,
f: `4/44/Bongo_Grave.ogg`,
g: `b/b4/Sting.ogg`,
// ... add more key-sound URL pairs ...
};
Object.keys(sounds).forEach(key => {
const btn = document.createElement("button");
document.querySelector("#drum-pad").appendChild(btn);
const sound = new Audio(baseURL + sounds[key]);
sounds[key] = sound;
btn.textContent = key;
btn.addEventListener("click", e => {
sound.currentTime = 0;
sound.play();
});
});
document.addEventListener("keydown", e => {
if (sounds[e.key]) {
sounds[e.key].currentTime = 0;
sounds[e.key].play();
}
});
<div id="drum-pad"></div>

Twice keypress different function running

l have 2 functions, l want to do like when the first keydown run one function than if its another keydown run the second function!
It is play/pause video function by spaceBar!
document.addEventListener('keydown', function(e) {
if (e.keyCode == 32) {
pauseAllVideos();
} else {
playAllVideos();
}
});
Solution
Its all about saving a boolean value that lets you know if all videos are playing or not and then use it in the if statements.
Here is the code snippet:
let playing = false;
const playAllVideos = () => console.log('playing all videos');
const pauseAllVideos = () => console.log('paused all videos');
const playPause = e => {
let key = e.keyCode || e.which;//Get key
if(key == 32) { //Space key
if(playing) {//If playing turn all videos off
pauseAllVideos();
playing = false;
} else {//vise versa
playAllVideos();
playing = true;
}
}
}
window.addEventListener('keypress', playPause);
<p>Press Space</p>

Categories

Resources