Check if element is focused with Vanilla JS, not jQuery - javascript

I have this big class Search, which controls my search bar on my website. Now, when a input is focused, i dont want my s key (which pops out the search bar) to execute when a input is focused. I tried with document.activeElement, but then, the search bar wont even open, whilst the input not being focused. You can see it, under keydown event listener, under Events comment
class Search {
// Describe and create object
constructor() {
this.openButton = document.querySelectorAll('.js-search-trigger');
this.closeButton = document.querySelector('#close-button');
this.searchOverlay = document.querySelector('.search-overlay');
this.searchField = document.getElementById('search-term');
this.typingTimer;
this.events();
this.isSpinnerVisible = false;
this.resultsDiv = document.getElementById('search-overlay__results');
this.previousValue;
console.log(this.openButton);
}
// Events
events() {
this.openButton.forEach(e => {
e.addEventListener('click', () => {
this.openOverlay();
document.body.classList.add('body-no-scroll');
});
})
this.closeButton.addEventListener('click', () => {
this.closeOverlay();
document.body.classList.remove('body-no-scroll');
})
document.addEventListener('keydown', (e) => {
if(e.key === 's' && !(this === document.activeElement)){
this.openOverlay();
document.body.classList.add('body-no-scroll');
console.log("s pressed")
}
if(e.key === 'Escape' && this.isOverlayOpen){
this.closeOverlay();
document.body.classList.remove('body-no-scroll');
console.log("esc pressed");
}
});
this.searchField.addEventListener('keyup', () => {
this.typingLogic();
})
}
// Methods
openOverlay(){
this.searchOverlay.classList.add('search-overlay--active');
this.isOverlayOpen = true;
}
closeOverlay(){
this.searchOverlay.classList.remove('search-overlay--active');
}
typingLogic(){
if(this.searchField.value != this.previousValue){
clearTimeout(this.typingTimer);
if(this.searchField.value){
if(!this.isSpinnerVisible){
this.resultsDiv.innerHTML = '<div class="spinner-loader"></div>';
this.isSpinnerVisible = true;
}
this.typingTimer = setTimeout(this.getResults(),2000);
}else{
this.resultsDiv.innerHTML = '';
this.isSpinnerVisible = false;
}
}
this.previousValue = this.searchField.value;
}
getResults(){
this.typingTimer = setTimeout(()=> {
this.resultsDiv.innerHTML = 'Some here';
this.isSpinnerVisible =false;
},2000)
}
}
export default Search

You can check tagName property of activeElement. And if it is not input then proceed with your code. Update your condition like below.
if(e.key === 's' && document.activeElement.tagName.toLowerCase() != 'input')

Related

Keep focus inside the modal while open

I want to keep the focus inside the modal with its respective focusable elements, I'm working with "WEB COMPONENTS, DOW AND SHADOW DOW"
when trying to perform this code snippet, focus still continues to show error and go to elements outside the modal
private setFocus(): void {
const focusableElements = this.querySelectorAll('a[href], button, details, input, select, textarea, [tabindex]:not([tabindex="-1"]), ani-button, ani-textfield');
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
this.addEventListener('keydown', event => {
const isTabbed = event.key === 'Tab' || event.code === '9';
if (!isTabbed) {
return;
}
if (event.shiftKey) {
if (document.activeElement === firstFocusableElement) {
(lastFocusableElement as HTMLElement).focus();
event.preventDefault();
}
} else {
if (document.activeElement === lastFocusableElement) {
(firstFocusableElement as HTMLElement).focus();
event.preventDefault();
}
}
});
(firstFocusableElement as HTMLElement).focus();
}
open(): void {
// this.hidden = false;
this.setAttribute('visible', 'true');
this.setFocus();
}

document.addEventListener in function not working

I'm trying to hide the "modal" box when the user press Esc key.
So, I first check where the box contains class - 'hidden', which
technically hide the box in UI.
Then if it's not hidden (the box does not contain class - 'hidden') and
appearing on screen, the function will wait for the Esc key for the
box to be disappeared.
Showing and hiding the box parts working just fine, but document.addEventListener part is not working.
const btnopenModal = document.querySelectorAll('.show-modal');
const btnCloseModal = document.querySelector('.close');
const overlay = document.querySelector('.overlay');
const modal =document.querySelector('.modal');
const showModal = function() {
modal.classList.remove('hidden');
overlay.classList.remove('hidden');
};
const hideModal = function() {
modal.classList.add('hidden');
overlay.classList.add('hidden');
}
for(let i = 0; i < btnopenModal.length; i++)
btnopenModal[i].addEventListener('click', showModal);
btnCloseModal.addEventListener('click', hideModal);
overlay.addEventListener('click', hideModal);
if(!overlay.classList.contains('hidden')) {
document.addEventListener('keypress', function(e) {
console.log(e.key);
if(e.key === 'Escape') {
hideModal();
}
})
};
Any other way around for this to work?
I would think that your if statement is evaluated when the webpage first runs, and my guess is that the if statement evaluates to false as it probably does contain the class "hidden" at first. I don't understand why you put it the key handler inside of an if statement, if it is for safety you should put it inside your function like so:
document.addEventListener('keypress', function(e) {
if(!overlay.classList.contains('hidden')) {
console.log(e.key);
if(e.key === 'Escape') {
hideModal();
}
};
})
Move if condition into callback. You want to always add keypress listener, just do not execute hideModal() if !overlay.classList.contains('hidden')
const btnopenModal = document.querySelectorAll('.show-modal');
const btnCloseModal = document.querySelector('.close');
const overlay = document.querySelector('.overlay');
const modal =document.querySelector('.modal');
const showModal = function() {
modal.classList.remove('hidden');
overlay.classList.remove('hidden');
};
const hideModal = function() {
modal.classList.add('hidden');
overlay.classList.add('hidden');
}
for(let i = 0; i < btnopenModal.length; i++)
btnopenModal[i].addEventListener('click', showModal);
btnCloseModal.addEventListener('click', hideModal);
overlay.addEventListener('click', hideModal);
document.addEventListener('keypress', function(e) {
console.log(e.key);
if(e.key === 'Escape' && !overlay.classList.contains('hidden')) {
hideModal();
}
});

Javascript dropdown immediatly closes itself when clicking on a value inside of it

I'm converting a dropdown from JQuery to vanilla JS, and I'm having some issues when I click on the dropdown it opens and I can select a value inside of it, but when I click on said value the value will be selected and it will instantly close.
I managed to narrow the problem down to this line
if($('*[data-toggle], *[data-totoggle]').is(e.target) || $('*[data-toggle], *[data-totoggle]').has(e.target).length){
return false;
}
In my vanilla js I replaced the above jquery code with this
if (document.querySelector('*[data-toggle], *[data-totoggle]') === e.target) {
return false;
}
This does not seem to achive the same thing as the jquery has and is functions, are there any equivalent of those in vanilla JS ?
codesandbox example > https://codesandbox.io/s/little-frost-tzefn?file=/src/index.js
Vanilla js version :
(function () {
document.querySelectorAll('*[data-toggle]').forEach(element => element.addEventListener('click', function (e) {
e.preventDefault();
const toToggle = element.dataset.toggle
// Close all other toggle elements and toggle the one you clicked
document.querySelectorAll('*[data-totoggle], *[data-toggle]').forEach(function (toggleElements) {
if (toggleElements.dataset.toggle === toToggle || toggleElements.dataset.totoggle === toToggle) {
toggleElements.classList.toggle('active')
} else if (!toggleElements.classList.contains('onlyClick')) {
toggleElements.classList.remove('active');
}
})
// toggle text if needed
if (element.dataset.toggletext) {
const toggleText = element.dataset.toggletext.split(',')
if (element.classList.contains('active')) {
element.html(toggleText[0])
} else {
element.html(toggleText[1])
}
}
// Remove toggle function from permanent elements
if (element.classList.contains('permanent')) {
element.querySelector('*[data-toggle="' + toToggle + '"]').removeAttribute('data-toggle');
element.querySelector('*[data-totoggle="' + toToggle + '"]').removeAttribute('data-totoggle');
}
}))
// close elements when clicking outside of them
document.addEventListener('mouseup', function (e) {
if (document.querySelector('*[data-toggle], *[data-totoggle]') === e.target) {
return false;
}
document.querySelectorAll('*[data-toggle], *[data-totoggle]').forEach(function (element) {
if (!element.classList.contains('onlyClick')) {
element.classList.remove('active')
}
})
if (document.querySelector('*[data-slidetoggle], *[data-toslidetoggle]') === e.target) {
return false;
}
document.querySelectorAll('*[data-slidetoggle]').forEach(function (element) {
if (!element.classList.contains('onlyClick')) {
element.classList.remove('active')
$('*[data-toslidetoggle]').slideUp();
}
})
});
// slide toggle elements
document.querySelectorAll('*[data-slidetoggle]').forEach(element => element.addEventListener('click', function () {
const slideToggle = this.dataset.slidetoggle
this.classList.toggle('active');
$('*[data-toslidetoggle="' + slideToggle + '"]').slideToggle(200);
if (this.dataset.toggletext) {
const toggleText = this.dataset.toggletext.split(',')
if (this.classList.contains('active')) {
this.html(toggleText[0])
} else {
this.html(toggleText[1])
}
}
}))
document.querySelector('*[data-trigger]').addEventListener('click', function (e) {
const toTrigger = e.target.dataset.trigger
document.querySelector('.' + toTrigger).trigger('click');
})
})();

Why does my jQuery app work ONLY after page refresh?

Problem Summary: jQuery todo list app offers 3 functions; add items, edit items, and remove items. I can add items continuously, but cannot edit or remove items continuously. A single item can be edited or removed at a time. A page refresh is required to remove or edit another item. The goal is to have all 3 functions working without having to refresh the page. There are no error messages displayed in the console.
What I have tried: I have attempted to remove event listeners with .off() at the completion of a function and then reinitialize the event listeners after the fact. This does not seem to help or make things worse.
Live Demonstration: codepen.io
jQuery:
function checkTodos() {
// todos in the key of my localStorage
let dataInLocalStorage = localStorage.getItem("todos");
let todos;
// check if it is null or not
if (dataInLocalStorage == null) {
todos = [];
$('#notices').html(`<div class='card'>No list items exist yet. Use the input box on the left to add items.</div>`);
} else {
todos = JSON.parse(dataInLocalStorage);
let html = "";
todos.forEach((todo, index) => {
html += `<div id='item-${index}' class='card' data-index='${index}'>${todo}</div>`;
});
$(".incomplete").empty().append(html);
$('#notices').empty();
}
}
$(document).ready(function() {
checkTodos();
// adding items in todos
$("input").keydown((e) => {
if (e.code === 'Enter' && $("input").val() !== "") {
todo = $("input").val();
let todosData = localStorage.getItem("todos");
if (todosData == null) {
todos = [];
} else {
todos = JSON.parse(todosData);
}
todos.push(todo);
localStorage.setItem("todos", JSON.stringify(todos));
$("input").val("");
}
checkTodos();
});
// list item removal
$('.incomplete > div').click((e) => {
let id = $(e.target).attr('id');
let selector = '#' + id;
let todosData = localStorage.getItem('todos');
let index = $(selector).attr('data-index');
todos = JSON.parse(todosData);
if (e.shiftKey) {
if (confirm("Remove the list item?")) {
todos.splice(index, 1);
localStorage.setItem('todos', JSON.stringify(todos));
checkTodos();
}
}
});
// list item editing
$('.incomplete > div').click((e) => {
let id = $(e.target).attr('id');
let selector = '#' + id;
let k = $(selector).attr('data-index');
let todosData = localStorage.getItem('todos');
todos = JSON.parse(todosData);
if (e.altKey) {
$(selector).attr('contenteditable','true');
$(selector).keydown(function(evt) {
if (evt.code === 'Enter') {
$(selector).removeAttr('contenteditable');
todos[k] = $(selector).html();
localStorage.setItem('todos', JSON.stringify(todos));
checkTodos();
}
});
}
});
});
Currently your event handlers are only registered for items that exist when loading the page (after the first call to checkTodos()). You can use event delegation to also handle events on dynamically added items by replacing
$('.incomplete > div').click((e) => { ... })
$('input').keydown((e) => { ... })
with
$(document).on('click', '.incomplete > div', (e) => { ... })
$(document).on('keydown', 'input', (e) => { ... })
etc.

Turning on and off an element's onclick properties

I'm trying to delay the load of a pop-up on a grid of images but want to prevent the ability to click on other images when this happens. Howver if I turn off onclick 'item.onclick = false', I don't seem to be able to turn it back on when the pop-up is turned back on? see line 'item.onclick = true'. Have also tried disabled = true/false but to no avail. Any suggestions?
var caseStudies = document.querySelectorAll('.posterImage');
var caseHover = document.querySelectorAll('.caseHover');
var modal = document.querySelectorAll('.modal');
caseStudies.forEach((button, index) => {
if ((isMobile == true) || (isTablet == true)) {
button.onclick = function(event) {
caseStudies.forEach((item) => {
item.onclick = false;
console.log(item);
});
caseHover.forEach((item) => {
item.classList.add('eventsNone');
console.log(item);
});
setTimeout(function(){
console.log("loading");
modal[index].style.display = "block";
// When the user clicks anywhere outside of the modal, close it (needs to live inside the button.onclick)
window.onclick = function(event) {
if (event.target == modal[index]) {
modal.forEach((item) => {
item.style.display = "none";
});
caseStudies.forEach((item) => {
item.onclick = true;
});
}
}
}, 500);
}
}
else
{
button.onclick = function(event) {
console.log("route2");
modal[index].style.display = "block";
caseStudies.forEach((item) => {
item.classList.add('eventsNone')
});
// When the user clicks anywhere outside of the modal, close it (needs to live inside the button.onclick)
window.onclick = function(event) {
if (event.target == modal[index]) {
modal.forEach((item) => {
item.style.display = "none";
});
caseStudies.forEach((item) => {
item.classList.remove('eventsNone')
});
};
};
};
};
});
Use an inline onclick = "function()" to set your onclick.
When disabling your onlick do it with element.onclick = null.
And enable it again with element.onclick = "function()"
Sorry for getting it wrong before I miss read it and thought you were doing it with buttons.
Also here is a duplicate question how to disable or enable all onClick for images on a page

Categories