Am doing the destructing right? - javascript

I have a coding challenge, which am sure am getting it right , but the challenge requires that certain steps need to be in account before moving forward. And i seem to be skipping a step which is to de-structure a value from a target of an event(hope am saying it right)
The question is to get the expected event parameter to the target property with a de-structure.
Is my code wrong?
const displaySelectedUser = (event) => {
var newcal = event.target.value;
console.log(newcal);
console.log((event.target.value));
var user = getSelectedUser(newcal);
var properties = Object.keys(user);
console.log(properties);
properties.forEach(prop => {
const span = document.querySelector(`span[data-${prop}-value]`);
if (span) {
span.innerText = user[prop];
}
});
};

It's not wrong - but the only practical opportunity you have for destructuring is here:
var newcal = event.target.value;
Do this:
var { target: { value: newcal } } = event;
Because you're not using the other properties of event, you could move this up to the function declaration instead:
const displaySelectedUser = ({ target: { value: newcal } }) => {...}
Now newcal will already be defined as event.target.value in your function.

Related

events with addEventListener

I'm tried to something like : Catch all events in my addEventListeners and then call my function calculateBill. I have a problem my events are lost. It is possible to pass the parameters for each addeventlister separately. I try to do it since few hours and i have no idea what's happening here.
const billInput = document.querySelector('#bill');
const percentageButton = document.querySelectorAll('.tip__values--item');
const numberOfPeople = document.querySelector('#people');
const tipAmount = document.querySelector('.result__amount--price');
const totalBill = document.querySelector('.result__total--price');
const reset = document.querySelector('button');
const catchBill = (e) => {
return e.target.value;
};
const catchPeople = (e) => {
return e.target.value;
};
const handleButtons = (e) => {
return e.target.textContent;
};
const calculateBill = (catchBill, catchPeople, handleButtons) => {
console.log(
'catchBill:',
catchBill,
'catchPeople:',
catchPeople,
'handleButtons:',
handleButtons
);
};
billInput.addEventListener('keyup', function (e) {
calculateBill(catchBill(e), catchPeople, handleButtons);
});
numberOfPeople.addEventListener('keyup', function (e) {
calculateBill(catchBill, catchPeople(e), handleButtons);
});
percentageButton.forEach((btn) => {
btn.addEventListener('click', function (e) {
calculateBill(catchBill, catchPeople, handleButtons(e));
});
});
I know that i can do this FrontedMentor challenge in other way but I'd like to know is this possible to do it that way. I know that problem is with parameters that i call in addeventlistener. How can i get my parameters in my calculateBill function with all events?
You'll need to cache the result of each event somewhere so that you can retrieve them later, or retrieve the value in every input each time any event takes place. It looks like calculateBill expects arguments to be strings, not functions, so passing catchBill, catchPeople, or handleButtons (which are functions) to it doesn't make sense. Consider something like:
// assign default values here if you want
let billValue;
let numPeople;
let percValue;
const handleBillChange = (e) => {
billValue = e.target.valueAsNumber;
calculateBill();
};
const handlePeopleChange = (e) => {
numPeople = e.target.valueAsNumber;
calculateBill();
};
const handlePercentageChange = (e) => {
percValue = e.target.textContent;
calculateBill();
};
billInput.addEventListener('keyup', handleBillChange);
numberOfPeople.addEventListener('keyup', handlePeopleChange);
billInput.addEventListener('keyup', handleBillChange);
// This is a NodeList, not a button. Consider naming the variable more precisely.
// percentageButtons, perhaps?
percentageButton.forEach((btn) => {
btn.addEventListener('click', handlePercentageChange);
});
While it'd be technically possible to store the previous events instead, and then do something like
billInput.addEventListener('keyup', function (e) {
calculateBill(catchBill(e), catchPeople(previousPeopleEvent), handleButtons(previousButtonEvent));
});
for all the inputs and buttons, that'd be extremely strange. Better to store just the values.

How to make a function change only the first array element

I'm quite new to JavaScript, and I'm stuck with an issue which I didn't manage to find an answer for.
I have an array, each item of which is tied to a card in markup.
When a button is pressed, a form pops up and allows to enter name and url for the new image, which is then unshifted to thebeginning of the array.
The last element of the array is popped, so there's always the same number of elements.
And the issue itself is:
When I add a single card, it works perfect: last card is popped and the new one is tucked in the beginning.
But when I use and submit the form again, the card that I have previously edited changes as well, although it should not.
I suspect that the mistake lies in the function that updates the cards, but I'm all out of ideas how to fix it.
Is there a way to fix that function?
const formAdd = document.querySelector('.popup__form-add')
const newElement = { name: '', link: '' };
const blank = { name: '', link: '' };
const placeName = document.querySelector('.popup__edit_type_place-name');
const placeImage = document.querySelector('.popup__edit_type_place-picture');
const elements = document.querySelector('.elements');
const cards = elements.querySelectorAll('.element');
const buttonAdd = document.querySelector('.profile__button-add');
const buttonAddClose = document.querySelector('.popup__add-button-close');
//content popup functions
function popupAddDisplay() {
popupAdd.classList.add('popup_opened');
}
function popupAddHide() {
popupAdd.classList.remove('popup_opened');
}
function contentUpdate() {
event.preventDefault();
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
initialCards.unshift(blank);
blank.name = newElement.name;
blank.link = newElement.link;
console.log(initialCards);
cardsCheck();
popupAddHide();
};
//cards array, page content loads from here
const initialCards = [{
name: 'Архыз',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/arkhyz.jpg'
},
{
name: 'Челябинская область',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/chelyabinsk-oblast.jpg'
},
{
name: 'Иваново',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/ivanovo.jpg'
},
{
name: 'Камчатка',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/kamchatka.jpg'
},
{
name: 'Холмогорский район',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/kholmogorsky-rayon.jpg'
},
{
name: 'Байкал',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/baikal.jpg'
}
];
//compares info from the markup with the array, then pushes array info into markup
function cardsCheck() {
cards.forEach(function(item, index) {
const nameAlt = initialCards[index].name;
const imageSrcAlt = initialCards[index].link;
item.querySelector('.element__name').textContent = nameAlt;
item.querySelector('.element__image').src = imageSrcAlt;
if (nameAlt == '') {
item.style.display = 'none'
}
console.log(nameAlt);
});
}
document.onload = cardsCheck();
buttonAdd.addEventListener('click', () => {
popupAddDisplay()
});
buttonAddClose.addEventListener('click', popupAddHide);
formAdd.addEventListener('submit', contentUpdate);```
You always use the same blank object. So at the end you have the same object multiple times in the array. If you change an attribute of this object it is changed wherever it is in the list also.
To avoid this you need to create a new object before adding it to the array
function contentUpdate() {
event.preventDefault();
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
let blank = { name: '', link: '' };
initialCards.unshift(blank);
blank.name = newElement.name;
blank.link = newElement.link;
console.log(initialCards);
cardsCheck();
popupAddHide();
};
Every time you create a new card, it seems like you override the global variables blank and newElement.
However, you don't have to. You could create a local variable in your contentUpdate function called newElement.
That way, each time updateContent is called, a new local variable is created.
You could therefore try the following code:
const formAdd = document.querySelector('.popup__form-add')
const placeName = document.querySelector('.popup__edit_type_place-name');
const placeImage = document.querySelector('.popup__edit_type_place-picture');
const elements = document.querySelector('.elements');
const cards = elements.querySelectorAll('.element');
const buttonAdd = document.querySelector('.profile__button-add');
const buttonAddClose = document.querySelector('.popup__add-button-close');
//content popup functions
function popupAddDisplay() {
popupAdd.classList.add('popup_opened');
}
function popupAddHide() {
popupAdd.classList.remove('popup_opened');
}
function contentUpdate() {
event.preventDefault();
const newElement = {}
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
initialCards.unshift(newElement);
console.log(initialCards);
cardsCheck();
popupAddHide();
}

Change Object Index

Is there way to change the index dynamically? or rebuild this object to where the 1 will be the Id of what ever object get passed into the function? Hope this makes sense.
export const createTree = (parentObj) => {
//keep in memory reference for later
const flatlist = { 1: parentObj }; <---- change the 1 based on parent.Id
...
}
My attempt thinking it would be easy as:
const flatlist = { parentObj.Id: parentObj };
Use computed property names to create a key from an expression:
const createTree = (parentObj) => {
const flatlist = { [parentObj.id]: parentObj };
return flatlist;
}
console.log(createTree({ id: 1 }));

Adding a class to a created element

The parameter of my function is a function. It should create an element but I should still be able to add attributes by using the parameter details.
E.g.:
const addElement = (details) => {
const element = document.createElement('div');
}
addElement(function() {
element.id = 'my-div'; // Not working since element is not defined
});
Well, I have tried to store the element in an object to be able to use it outside of that function.
let element = {};
const displayVideo = (type, details) => {
element = document.createElement(type);
element.width = 200;
element.height = 200;
element.classList.add('my-class'); // <--- THE PROBLEM!
if (details) {
details();
}
document.querySelector('#layer').insertBefore(element, document.querySelector('#el'));
};
displayVideo('VIDEO', function () {
element.controls = true;
});
My element can not be created because of element.classList.add('my-class'); and I don't even get an error message. If I remove that line, it works but I would still like to be able to add a class to that object. How can I do this?
Just pass element into the function. Since you're just editing properties on the object, this won't cause reference vs value errors.
const addElement = (details) => {
const element = document.createElement('div');
if (details) details(element);
return element;
}
const ele = addElement(function(element) {
element.id = 'my-div';
});
console.log(ele);
In this case details could be something like classname.
function element(type, classname) {
var element = document.createElement(type);
if (classname !== undefined) {
element.classList.add(classname);
}
return element;
};
element("div","my-class"); //<div class="my-class"></div>
Of course instead of classname you could use an array or an object and loop through in order to set multiple attributes.
Or you could store the return value of your function in a variable and then add all the attributes:
var myelement = element("div");
myelement.classList.add("my-new-class");
myelement //<div class=​"my-new-class">​</div>​

Javascript (ES6), destructure based off variable

I am wondering to see if there is a way to destructure objects in javascript with using a variable. Where as I was doing something like this in my function -
mutateTaxon(data) {
const { content } = data;
const { plp } = content || {};
...
This was working fine, however I need to expand this function based off another factor that can change if I need to use data.content (which it is using now) or data.collection. So I have another node on the data - which changes call to call. I am trying something like this -
mutateTaxon(data) {
const match = lowerCase(data.taxonomy.name);
const { match } = data;
const { plp } = match || {};
Where that match variable would evaluate to either content or collection (as expected). This does not seem to work however, maybe it is not possible? I was thinking maybe the match var needed to be evaluated so I tried something like -
const { [[match]] } = data;
which also is not working. Maybe this is not possible, or I am approaching this wrong. I am wondering, is something like this possible? Thanks!
The destructuring syntax would, as Jonas W. said, be a bit more cumbersome than the bracket notation, but nonetheless, this is how you would do it:
mutateTaxon(data) {
const key = lowerCase(data.taxonomy.name);
const { [key]: { plp } = {} } = data;
Demo
const foo = { bar: { plp: 'success' } }
const key = 'bar'
const { [key]: { plp } = {} } = foo
console.log(plp)
And to confirm that default parameter = {} works as expected:
const foo = { }
const key = 'bar'
const { [key]: { plp } = {} } = foo
console.log(plp)
const key = lowerCase(data.taxonomy.name);
const match = data[key];
I dont think that object destructuring is useful here. But if you need that:
const key = lowerCase(data.taxonomy.name);
const {[key]: match} = data;

Categories