I have 2 divs with same class but different textContent
I want to extracts its value using eventListners and pass it as a argument to another function
Html Code
<div class = "seasonDeatils__container">
<p class = "seasonYear ">2020</p>
</div>
<div class = "seasonDeatils__container">
<p class = "seasonYear ">2019</p>
</div>
JavaScript Code I tried
var season;
const getSeasonYear = document.querySelectorAll('.seasonDeatils__container');
getSeasonYear.forEach((el)=>{
el.addEventListener('click', ()=>{
season = el.firstElementChild.textContent;
})
})
//I now want to access the 'season' value elsewhere in the code
Just pass the value of season as an argument from inside your click listener function itself to a function elsewhere in your code as follows:
var season;
const getSeasonYear = document.querySelectorAll('.seasonDeatils__container');
getSeasonYear.forEach((el)=>{
el.addEventListener('click', ()=>{
season = el.firstElementChild.textContent;
someOtherFunction(season);
})
})
someOtherFunction = x => {
console.log(x);
alert(x);
}
<div class = "seasonDeatils__container">
<p class = "seasonYear ">2020</p>
</div>
<div class = "seasonDeatils__container">
<p class = "seasonYear ">2019</p>
</div>
Related
I have some To Do-s that are dinamically created by the user:
<div>
<h3 class = 'taskTitle'>do homework </h3>
<p class = 'taskDate'>Expires: 2021.12.31</p>
<input type = button class = 'delBtn' value = 'x'></input>
<input type = button class = 'expandBtn' value = '...'></input>
</div>
<div>
<h3 class = 'taskTitle'>workout </h3>
<p class = 'taskDate'>Expires: 2021.10.11</p>
<input type = button class = 'delBtn' value = 'x'></input>
<input type = button class = 'expandBtn' value = '...'></input>
</div>
**etc.**
On click of the expandBtn a pop up window appears that would contain the title (h3) and the date (p) of the specific To Do.
script:
function showDescription(){
const expandBtns= document.querySelectorAll('.expandBtn')
expandBtns.forEach(btn => {
btn.addEventListener('click', function(event){
let popUp = document.createElement('div')
popUp.classList.add('descriptionBox')
let title = event.target.parentNode.firstChild.textContent **<--says its undefined**
let date = event.target.parentNode.firstChild.textContent **<--says its undefined**
popUp.innerHTML = `
<h3>${title}</h3>
<p class = 'description'> lorem ipsum </p>
<p class = 'dateDescription'>${date}</p>
<input class = 'delDescription' type = button value = 'x'></input>`
const todos = document.querySelector('#todos')
todos.appendChild(popUp)
//close button for popUp
const delDescription = document.querySelectorAll('.delDescription')
delDescription.forEach(btn => {
btn.addEventListener('click', function (event){
event.target.parentNode.remove()
})
})
// alert(document.querySelector('.activeProject').textContent)
})
})
}
So how could I target them? querySelector isn't good either, as I have more than 1 To Do-s. Any help appreciated!
You could add a unique id or class to all the div elements. For example:
<div id="to-do-1"><p>Test</p></div>
<button onclick="myFunction()">Click Me!</button>
<script>
function myFunction() {
document.getElementById("to-do-1").style.display = "inline";
}
</script>
<style>
#to-do-1 {
display: none;
}
</style>
Select all expandBtn
var btn = document.getElementsByClassName("expandBtn")
Create a loop and addEventListener for all of them
for(let i =0; i < btn.length; i++) {
btn[i].addEventListener("click", function () {
let h3 = this.parentNode.getElementsByTagName("h3")[0];
let p = this.parentNode.getElementsByTagName("p")[0];
alert("h3 = "+ h3.innerText + " & p = " + p.innerText);
}
}
now when the user clicking on anyone of them it's will search for h3, p who is belong to the parent of this button
Using .children will get you what you want:
document.addEventListener("click",ev=>{
if (ev.target.className!=="expandBtn") return;
const [name,date]=[...ev.target.parentNode.children].slice(0,2).map(e=>e.textContent)
console.log(name,date);
// further code for popup ...
})
<div>
<h3 class = 'taskTitle'>do homework </h3>
<p class = 'taskDate'>Expires: 2021.12.31</p>
<input type = button class = 'delBtn' value = 'x'></input>
<input type = button class = 'expandBtn' value = '...'></input>
</div>
<div>
<h3 class = 'taskTitle'>workout </h3>
<p class = 'taskDate'>Expires: 2021.10.11</p>
<input type = button class = 'delBtn' value = 'x'></input>
<input type = button class = 'expandBtn' value = '...'></input>
</div>
.firstChild will return the first childNode, which in your case would have been a blank and a line-break and not the <h3> element you expected.
.children on the other hand will return a collection of all child elements (like: <div>, <p>, <span>, ... etc.), the chained .slice(0,2) will slice off the first two elements only.
I have two different functions and I created a third function which I use to display some pieces of information. What I am trying to do is: I want to display photographerPrice which is inside of the photographerProfil function and the sum of all the likes which is inside of the photographerWork function, those two elements I want to display inside my third function likesAndPrice.
I tried to call the function likesAndPrice inside two of the functions photographerProfil and photographerWork using two arguments and two parameters: it doesn't work, it only works when I use one single parameter either for likes or the price but not both. How can I display the two of them Price and Likes in one place ?
The result I want:
photographerProfil Function code
function photographerProfil(JsonData){
const id = window.location.search.split('id=')[1];
const photographers = !id ? JsonData.photographers : JsonData.photographers.filter(photographer => photographer.id == id);
photographers.forEach(element => {
const domDiv = document.getElementById('photographer-container');
const newDiv = document.createElement('div');
const photographerPrice = element.price;
const profilTemplate = `
<div class="profil-container">
<h2>${element.name}</h2>
<p>${element.city}, ${element.country}</p>
<p class="tagline">${element.tagline}</p>
<p>${element.tags.map(tag => `<button class='tags'>#${tag}</button>`).join(" ")}</p>
<button id="test">Contactez-moi</button>
<div class="photoBox">
<img src="${element.portrait}" alt="photo">
</div>
</div>
`
newDiv.innerHTML = profilTemplate;
domDiv.appendChild(newDiv);
showModal(); // function invoked here
photographerWork(JsonData, element) // function invoked here
})
}
photographerWork Function code
function photographerWork(JsonData, homeElement){
let sum = 0;
const homeElt = homeElement.id;
JsonData.media.forEach(element => {
if(homeElt == element.photographerId){
const domDiv = document.getElementById('photographer-work');
const newDiv = document.createElement("div");
sum += element.likes;
const workTemplate = `
<div class="photo-box">
<div class="photo">
${videoOrImage(element.image, element.video, element)}
</div>
<div class="text">
<p> ${element.tags}<b>${element.price} €   ${element.likes} <i class="fas fa-heart"></i></b></p>
</div>
</div>
`
newDiv.innerHTML = workTemplate;
domDiv.appendChild(newDiv);
}
})
likesAndPrice(sum) // function invoked here
}
And the Function in which I want to display the Likes and Price
//total likes and price
function likesAndPrice(sum){
const domDiv = document.getElementById('photographer-work');
const newDiv = document.createElement("div");
const likesAndPriceTemplate = `
<span>${sum} <i class="fas fa-heart"></i></span>
<span></span>
`
newDiv.classList.add('likesAndPriceContainer')
newDiv.innerHTML = likesAndPriceTemplate;
domDiv.appendChild(newDiv)
}
Assuming you can also call likesAndPrice() in your photographerProfil function
Inside photographerWork at the last line you can return the local sum variable
return sum;
Now when you invoke the function at photographerProfil it returns the sum
let sum = photographerWork(JsonData, element) // function invoked here
Now you have your sum and the photographerPrice both in your photographerprofil function available this means you can call here the likesAndPrice function.
likesAndPrice(sum, photographerPrice) // function invoked here
Note: you have to update the signature where you define the function likeAndPrice() and add another parameter to it
function likesAndPrice(sum, <param>){
In vanilla JavaScript how can I bind an element to an object so that if a child element of the object has a value I can update the object with it? Need to be compatible with IE10+ and all other browsers.
With a button I am dynamically adding an element (createElement()) containing a form input. When the element is created it also creates an object that should also be the element so I can update the object with the input value on change.
I then store each new object in an array.
The issue I am having is connecting the input value with the correct object. I tried looping through the array hoping to update each object in turn with the current input value of the event target but couldn't succeed. I tried registering the element (deprecated) and various other things but I cannot quite workout how to link the input to the container object (lineObject).
I could really use some help solving this problem and understanding how to bind an element to an object in the way I need.
//lineNumber *** //
let lineNumber = document.querySelectorAll('.lineNumber');
let numberOfLines = lineNumber.length;
//first instance of input element
let lineText = document.querySelector('.lineText');
//first input value of element
let lineTextValue = document.querySelector('input[name="lineText"]').value;
//create initial lineObject for first line
let lastLine = lineNumber[numberOfLines - 1];
let lineContainer;
//lineNumber object constructor
function LineObject(lineText, writable) {
//set properties
this.lineText = lineText;
this.writable = writable;
}
//new object at new lineNumber element, set values
let lineObject = new LineObject(lineTextValue, true);
//create array containing initial line object
let lineArray = [lineObject];
//line functions
(function lineGeneration(){
//add or remove lines
document.addEventListener('click', function(e) {
//this
let self = e.target;
// has class .addLine
if (hasClass(self, 'addLine')) {
//call function to get variables
insertLineHTML();
//clone new line after the last line\
self.parentElement.parentElement.parentElement.parentElement.parentElement.appendChild(lineObject.cloneNode(true));
//lineNumber input location
let newlineTextInput = self.parentElement.parentElement.parentElement.parentElement.nextElementSibling.querySelector('input[name="lineText"]');
//input value of new element
let lineTextValue = newlineTextInput.value;//normally "" when created unless placeholder text
//new object at new lineNumber element
lineObject = new LineObject(lineTextValue, true);
//add new object to lineArray
lineArray.push(lineObject);
refreshLineNodeList();
}
});
//combine accordion / refresh
function refreshLineNodeList(){
//refresh number of elements in nodelist
lineNumber = document.querySelectorAll('.lineNumber');
//get new length
numberOfLines = lineNumber.length;
}
//line html and vars
function insertLineHTML(){
lineObject = document.createElement('div');
lineObject.setAttribute('class', 'lineNumber');
lineObject.innerHTML = `
<div class="accordion-title">
<h3>Line 2</h3>
</div>
<div class="input-section">
<div class="input-row">
<div class="input-container">
<label>Line 2 :</label>
<input type="text" name="lineText" value="" class="lineText">
</div>
<div class="input-row">
<div class="button-container">
<div class="warning"></div>
<button class="addLine">Add Another Line</button>
</div>
</div>
</div>`;
console.log(lineNumber);
}
})();
//lineText addEventListener update object value
document.addEventListener('keyup', function(e) {
let self = e.target;//input field
let lineTextValue = self.value;
// has class .lineText
if (hasClass(self, 'lineText')) {
//for each lineObject in LineArray
//lineArray.forEach(function(arrayObject) {
//update lineObject HTMLelement.prototype
Object.defineProperty(lineObject, 'lineText', {
//update object value to event target value
get: function() {
return this.lineTextValue;//how can I get the right lineObject object from the array when I update the input
},
set: function(lineTextValue) {
this.lineText = lineTextValue;//how can I aet the right lineObject object in the array when I update the input
}
});
//debugging
//console.log('objectProperty = ' + arrayObject.lineText);
console.log('this.lineText = ' + this.lineText);
console.log('Object.entries(lineObject) - ' + Object.entries(lineObject));
//console.log('lineObject.lineText = '+ lineObject.lineText);
//console.log('lineTextValue = '+ lineTextValue);
//});
};
});
let button = document.getElementById('test');
button.addEventListener( "click", testFunction );
function testFunction(){
button.addEventListener( "click", testFunction );
//console.log('Object.keys(lineObject) - '+ Object.keys(lineObject));
//console.log('Reflect.ownKeys(lineObject) - ' + Reflect.ownKeys(lineObject));
//console.log('Object.values - ' + Object.values(lineObject));
//console.log('lineObject = '+ lineObject.lineText);
//console.log('Object.entries(lineObject) - ' + Object.entries(lineObject));
//console.log('Object.entries(lineObjectClone) - ' + Object.entries(lineObjectClone));
//console.log('lineObjectClone.lineText = ' + lineObject.lineText);
//console.log('lineObjectClone[1].lineText = ' + lineObjectClone.lineText);
//console.log('lineArray[0] = ' + lineArray[0].lineText);
console.log('lineArray = ' + lineArray);
console.log('numberOfLines = ' + numberOfLines);
for(let i = 0; i < numberOfLines; ++i ){
console.log('lineArray[i].lineText = ' + lineArray[i].lineText)
}
};
//does the element have the class specified?
function hasClass(elem, className) {
return elem.classList.contains(className);
};
<section>
<button id="test">Test</button>
<div class="lineNumber">
<div class="accordion-title">
<h3>Line</h3>
</div>
<div class="input-section" style="display: block;">
<div class="input-row">
<div class="input-container">
<label>Line Text :</label>
<input type="text" name="lineText" value="" class="lineText">
</div>
</div>
<div class="input-row">
<div class="button-container">
<div class="warning"></div>
<button class="addLine">Add Another Line</button>
</div>
</div>
</div>
</div>
</section>
One way to do this is to use a closure.
The purpose of a closure is to capture variables from the containing function so those variables can be used later, after the containing function exits.
A simple example could look like this:
let data = {
nameGenerator: 0
};
function addInput() {
// generate a new name and property in data object
let propertyName = String.fromCharCode("a".charCodeAt() + data.nameGenerator++);
// initialize property value to its name
data[propertyName] = propertyName;
// add <div><input value="(property)"></div> to container
let containerElement = document.getElementById("container");
let lineElement = document.createElement("div");
let inputElement = document.createElement("input");
lineElement.appendChild(inputElement);
containerElement.appendChild(lineElement);
// initialize input value (note: this does not bind the two, just initializes)
inputElement.value = data[propertyName];
// create a closure that binds the property to the element
inputElement.addEventListener("keyup", function () {
// inside this function, propertyName and inputElement
// are "captured" in the closure
data[propertyName] = inputElement.value;
})
}
Note that the propertyName and inputElement variables are defined in the outer addInput function, but they are captured in the closure that is created when you assign the anonymous function as an event listener.
Here is a fiddle with a complete working example: https://jsfiddle.net/b3ta60cn/
From my understanding it's good practice to keep all your DOM selectors inside an object, because if the html is modified, you only need to change that object instead of looking through the entire code. However, how do you get a class name or a id from that object?
Example of what I used to do.
https://codepen.io/anon/pen/vvLoqJ?editors=1111
What I'am trying to do:
Obviously is not going to work because I attempting to add string with object. But if there is a way to add that string class place so that button 2 would work without modifying elements object. Thank you in advance.
https://codepen.io/anon/pen/WLweom?editors=1111
HTML
<button class = "button--roll">First Roll</button>
<button class = "button--roll-2">Second Roll</button>
SCRIPT
const elements = {
buttonRoll: document.querySelector(".button--roll"),
};
class buttonRoll{
constructor(){
// Properties
this.buttonRoll = elements.buttonRoll;
// Methods
this.events;
}
events(){
let roll = "-2";
// this.buttonRoll + roll.addEventListener('click', () => this.buttonRoll2());
console.log(this.buttonRoll);
}
buttonRoll2(){
console.log("second button is clicked");
}
}
buttonRoll = new buttonRoll;
buttonRoll.events();
in this case, by using your const element :
const elements = {
buttonRoll: document.querySelector(".button--roll"),
};
class buttonRoll {
constructor() {
let base_ClassName = elements.buttonRoll.className;
this.button_1 = elements.buttonRoll;
this.button_2 = document.querySelector('.'+base_ClassName+'-2');
this.button_1.onclick = (e)=>this.button_x_click(e);
this.button_2.onclick = (e)=>this.button_x_click(e);
}
button_x_click(e){
console.log(e.target.className);
}
}
buttonRolls = new buttonRoll();
<button class = "button--roll">First Roll</button>
<button class = "button--roll-2">Second Roll</button>
you're looking for something like that ?
class buttonRoll {
constructor(base_ClassName) {
this.button_1 = document.querySelector('.'+base_ClassName);
this.button_2 = document.querySelector('.'+base_ClassName+'-2');
this.button_1.onclick = (e)=>this.button_1_click(e);
this.button_2.onclick = (e)=>this.button_2_click(e);
}
button_1_click(e){
console.log('button_1_click');
}
button_2_click(e){
console.log('button_2_click');
}
}
buttonRolls = new buttonRoll('button--roll');
<button class = "button--roll">First Roll</button>
<button class = "button--roll-2">Second Roll</button>
I use the querySelector in JS to select html markup that I filled in with a JS script. However, anytime I try to store the divs with a class of .card in the const questionCard, I get null.
Why can't I select the cards?
HTML:
<div class='card-container'></div>
JS:
const questionBlock = document.querySelector('.card-container');
const questionCard = document.querySelector('.card');
function build() {
let output = [];
...some unimportant code here...
output.push(
`
<div class='card'>
<div class='question'>${current.question}</div>
<div class='answers'>${answers.join('')}</div>
</div>
`
);
})
questionBlock.innerHTML = output;
}
build();
You need to call document.querySelector('.card') after calling build(). It cannot find HTML elements that do not exist yet.
const questionBlock = document.querySelector('.card-container');
function build() {
let output = [];
...some unimportant code here...
output.push(
`
<div class='card'>
<div class='question'>${current.question}</div>
<div class='answers'>${answers.join('')}</div>
</div>
`
);
})
questionBlock.innerHTML = output;
}
build();
const questionCard = document.querySelector('.card');
An alternative to the more correct answers is:
const questionCard = document.getElementsByClassName('card');
now: questionCard is a live HTMLCollection, and questionCard[0] will be the first element with class including card