I am trying to build a responsive drop down navbar in React and have come to a roadblock. I have these variables defined in componentDidMount
componentDidMount = () => {
let nav = document.getElementById("topNav")
let main = document.getElementById("main")
let menu = document.getElementsByClassName("menuitems")
let close = document.getElementById("closeBtn")
nav.style.height = "50px";
main.style.marginTop = "50px";
for (let i = 0; i < menu.length; i++) {
menu[i].style.marginTop = "100px";
};
close.addEventListener("click", function () {
const menuIcon = close.children;
for (let i = 0; i < menuIcon.length; i++) {
menuIcon[i].classList.toggle("active");
}
});
}
and I am attempting to access them in an onClick function on the same react component here
navToggle = () => {
console.log(this.nav)
}
the variable is logged as undefined.
So, my question, how can I access variables that are defined in the compnentDidMount?
thank you
Variabled defined within the scope of componentDidMount will stay within that function itself. You need to assign them to the class instance like
componentDidMount = () => {
this.nav = document.getElementById("topNav")
this.main = document.getElementById("main")
this.menu = document.getElementsByClassName("menuitems")
this.close = document.getElementById("closeBtn")
this.nav.style.height = "50px";
this.main.style.marginTop = "50px";
for (let i = 0; i < menu.length; i++) {
this.menu[i].style.marginTop = "100px";
};
this.close.addEventListener("click", function () {
const menuIcon = this.close.children;
for (let i = 0; i < menuIcon.length; i++) {
menuIcon[i].classList.toggle("active");
}
});
}
Related
I have two click methods, which do the same thing, so I created an external function. But external function doesn't know what 'i' is. How can I make it work?
Passing 'i' as argument doesn't work.
const modalOverlay = document.querySelectorAll(".overlay");
const modalWindow = document.querySelectorAll(".modal_window");
const modalCloseBtn = document.querySelectorAll(".close_modal");
const modalOpenBtn = document.querySelectorAll(".open_modal");
const closeModal = ()=>{
modalOverlay[i].classList.add("hidden");
modalWindow[i].classList.add("hidden");
}
for (let i = 0; i < modalOpenBtn.length; i++) {
modalOpenBtn[i].addEventListener("click", ()=>{
modalOverlay[i].classList.remove("hidden");
modalWindow[i].classList.remove("hidden");
})
}
for (let i = 0; i < modalCloseBtn.length; i++) {
modalCloseBtn[i].addEventListener("click", closeModal)
}
for (let i = 0; i < modalOverlay.length; i++) {
modalOverlay[i].addEventListener("click", closeModal)
}
When I define a function outside the function, I cannot access the glide parameter:
export const setFocusListenersForKeyboardNavigation = (glide) => {
const slides = glide._c.Html.slides;
for (let i = 0; i < slides.length; i++) {
const currentSlide = slides[i];
const slideButton = currentSlide.querySelector(".js-slide-button");
const slideLink = currentSlide.querySelector(".js-slide-link");
slideButton && slideButton.addEventListener('focus', focusListener);
slideLink && slideLink.addEventListener('focus', focusListener);
}
};
const focusListener = (event) => {
const activeIndex = glide._i;
const buttonIndex = event.target.dataset.slideIndex;
if (activeIndex !== parseInt(buttonIndex)) {
glide.go(`=${buttonIndex}`);
}
};
Hence, I have did something like that:
export const setFocusListenersForKeyboardNavigation = (glide) => {
const focusListener = (event) => {
const activeIndex = glide._i;
const buttonIndex = event.target.dataset.slideIndex;
if (activeIndex !== parseInt(buttonIndex)) {
glide.go(`=${buttonIndex}`);
}
};
const slides = glide._c.Html.slides;
for (let i = 0; i < slides.length; i++) {
const currentSlide = slides[i];
const slideButton = currentSlide.querySelector(".js-slide-button");
const slideLink = currentSlide.querySelector(".js-slide-link");
slideButton && slideButton.addEventListener('focus', focusListener);
slideLink && slideLink.addEventListener('focus', focusListener);
}
};
I want to know if it is hack or good practice? Is there more convenient way for doing this.
Having the function outside is better.
Mainly for readability and testing, but if your function is called a lot of times (several hundreds for example) it can be even performance hit to be redefined every time.
you can add arrow function to the listener, that will call the focusListener with the correct parameters.
you can do something like this:
export const setFocusListenersForKeyboardNavigation = (glide) => {
const slides = glide._c.Html.slides;
for (let i = 0; i < slides.length; i++) {
const currentSlide = slides[i];
const slideButton = currentSlide.querySelector(".js-slide-button");
const slideLink = currentSlide.querySelector(".js-slide-link");
slideButton && slideButton.addEventListener('focus', (event) => {focusListener(event, glide)});
slideLink && slideLink.addEventListener('focus', (event) => {focusListener(event, glide));
}
};
const focusListener = (event, glide) => {
const activeIndex = glide._i;
const buttonIndex = event.target.dataset.slideIndex;
if (activeIndex !== parseInt(buttonIndex)) {
glide.go(`=${buttonIndex}`);
}
};
I'm trying to access objects properties inside an array in my code to render the text values of input boxes restored after a refresh from the local storage but for some reason when I try to run the for loop inside my appStart() function it gives me: "Uncaught TypeError: Cannot read property 'id' of undefined at appStart". Any insiights of why this happens and how to fix it will be greatly appreciated.
const currentDayPlaceholder = $("#currentDay");
const timeInTimeBlocks = $(".input-group-text");
const timeBlockInput = $(".form-control");
const saveButton = $(".saveBtn");
let numericCurrentTime = parseInt(moment().format("H A"));
let notes = [];
currentDayPlaceholder.append(moment().format('dddd, MMMM Do'));
function timeBlocksColorDeterminator() {
for (let i = 0; i < timeInTimeBlocks.length; i++) {
let numericTimeinTimeBlock = parseInt($(timeInTimeBlocks[i]).text());
if ($(timeInTimeBlocks[i]).hasClass('pm')) {
numericTimeinTimeBlock += 12;
}
if (numericCurrentTime === numericTimeinTimeBlock) {
$(timeBlockInput[i]).addClass("present");
} else if (numericCurrentTime > numericTimeinTimeBlock) {
$(timeBlockInput[i]).addClass("past");
} else {
$(timeBlockInput[i]).addClass("future");
}
}
}
function appStart() {
notes = JSON.parse(localStorage.getItem("timeBlockNotes"));
for (let i = 0; i < timeBlockInput.length; i++) {
if (i === parseInt(notes[i].id)) {
timeBlockInput[i].value = notes[i].value;
}
}
}
appStart();
saveButton.on("click", function () {
console.log("click");
notes.push({
value: timeBlockInput[this.id].value,
id: this.id
})
localStorage.setItem("timeBlockNotes", JSON.stringify(notes));
})
timeBlocksColorDeterminator();
I have fixed this after changing my appStart() function to this :
function appStart() {
notes = JSON.parse(localStorage.getItem("timeBlockNotes"));
for (let i = 0; i < notes.length; i++) {
timeBlockInput[parseInt(notes[i].id)].value = notes[i].value;
}
}
thank you guys for your comments and answers.
I created a module but it is not automatically rendered in my page, I have to manually call Board.renderBoard() in the console for the Board to appear. What am I doing wrong ?
here's the entire code:
const Board = (() => {
const board = document.querySelector('.board');
const createTiles = () => {
tile = document.createElement('div');
tile.classList.add('tile');
return tile;
};
const renderBoard = () => {
for (let i = 0; i < 9; i++) {
createTiles();
board.appendChild(createTiles());
}
};
return {
renderBoard
};
})();
I created a module but it is not automatically rendered in my page, I have to manually call Board.renderBoard() in the console for the Board to appear.
JS doesn't just call function unless you tell it to call these functions.
Your module simply defines an object Board with a method renderboard. There's nothing that tells JS to call this method.
You could add the call right after declaring the module.
const Board = (() => {
...
})();
Board.renderBoard();
But if all you want is that the code initially builds the board you can do:
(() => {
const board = document.querySelector('.board');
const createTiles = () => {
const tile = document.createElement('div'); // please declare your variables
tile.classList.add('tile');
return tile;
};
for (let i = 0; i < 9; i++) {
createTiles(); // what is this line for?
board.appendChild(createTiles());
}
})();
or
(() => {
const board = document.querySelector('.board');
for (let i = 0; i < 9; i++) {
const tile = document.createElement('div');
tile.classList.add('tile');
board.appendChild(tile);
}
})();
or maybe even
document.querySelector('.board').innerHTML = '<div class="tile"></div>'.repeat(9);
I have two functions that display and hide elements on a page. I'm trying to make the code more efficient so if elements get added to the page I don't have to update every line. This is how the code is now:
function myOpenFunction() {
var elem1 = document.getElementById("div1");
if (elem1) { document.getElementById("div1").style.display = "block";}
var elem2 = document.getElementById("div2");
if (elem2) { document.getElementById("div2").style.display = "block";}
var elem3 = document.getElementById("div3");
if (elem3) { document.getElementById("div3").style.display = "table";}
}
function myCloseFunction() {
var elem1 = document.getElementById("div1");
if (elem1) { document.getElementById("div1").style.display = "none";}
var elem2 = document.getElementById("div2");
if (elem2) { document.getElementById("div2").style.display = "none";}
var elem3 = document.getElementById("div3");
if (elem3) { document.getElementById("div3").style.display = "none";}
}
I tried putting all of those variables into an array outside of the function so that I don't have to repeat the variables if I add new elements. However, it doesn't work.
var myArray = [
document.getElementById("div1"),
document.getElementById("div2"),
document.getElementById("div3")
];
function myOpenFunction() {
if (myArray[0]) { myArray[0].style.display = "block";}
if (myArray[1]) { myArray[1].style.display = "block";}
if (myArray[2]) { myArray[2].style.display = "table";}
}
function myCloseFunction() {
if (myArray[0]) { myArray[0].style.display = "none";}
if (myArray[1]) { myArray[1].style.display = "none";}
if (myArray[2]) { myArray[2].style.display = "none";}
}
Is there anyway to do this?
Your myOpenFunction and myCloseFunctions are checking whether the elements exist at the time the function is called, whereas your myArray is putting the elements into the array when the script runs - the logic is not the same. You might use an array of ids, and then when the open or close function is called, iterate over the array to call getElementById to extract each element, and if it exists, set its style:
const ids = ['div1', 'div2', 'div3'];
function myOpenFunction() {
ids.forEach((id, i) => {
const element = document.getElementById(id);
if (element) {
element.style.display =
i === 2
? 'block'
: 'table';
}
});
}
What you can do is use a counter and concatenate with your class names to push to your array. You just need to have all your divs with IDs of div1, div2, etc:
var myElements = document.getElementsByClassName("selectedDivs");
var myArray = [];
for (var i = 1; I <= myElements.length; i++) {
myArray.push("div" + i);
}
Then use this in your function:
if (document.getElementById(myArray[I]) {
This will fix your problem.
It would be better to give all items the same class and loop other them. If you need different display states for some elements, you could specify them inside an html data attribute.
const myElements = document.querySelectorAll('.open-close');
function myOpenFunction() {
myElements.forEach(elem => {
elem.style.display = elem.dataset.display || 'block';
});
}
function myCloseFunction() {
myElements.forEach(elem => {
elem.style.display = 'none';
});
}
<div class="open-close">...</div>
<div class="open-close">...</div>
<div class="open-close" data-display="table">...</div>
EDIT: Because I am not shure about your JavaScript experience, here is a maybe more simple version with more classic methods and syntax:
var myElements = document.getElementsByClassName('open-close');
function myOpenFunction() {
for (var i = 0; i < myElements.length; i++) {
var elem = myElements[i];
var displayAttr = elem.getAttribute('data-display');
if (displayAttr !== null) {
elem.style.display = displayAttr;
} else {
elem.style.display = 'block';
}
}
}
function myCloseFunction() {
for (var i = 0; i < myElements.length; i++) {
myElements[i].style.display = 'none';
}
}