How do I check the attribute value of a div in React - javascript

I use the JavaScript code below to get and log the attribute value of a div.
I want to rewrite the JavaScript code using React. When I tried doing the same code in React. I get error: e.path.find is not a function
How The Function Works:
First,after clicking mealsEl, e.path.find is used to go through all the child elements of meals EL,
Then it returns the child elements containing the class Name: 'meal-info'
Then it checks if the child element containing class Name 'meal-info' also has an attribute of 'data-meal-id.
Then it logs the value of 'data-meal-id'
const mealsEL = document.getElementById('meals')
mealsEL.addEventListener('click', (e) => {
const mealInfo = e.path.find((item) => {
console.log(item)
if (item.classList) {
return item.classList.contains('meal-info');
} else {
return false;
}
});
console.log(mealInfo)
if (mealInfo) {
const mealID = mealInfo.getAttribute('data-mealid');
getMealByID(mealID);
}
});
<div class="container">
<div id="result-heading"></div>
<div id="meals" class="meals">
<div class="meal">
<img class="meal-img" src="https://source.unsplash.com/random" alt="" style="width:180px; height: 180px;border: solid #000 4px;"/>
<div class="meal-info" >
</div>
</div>
</div>
<div id="single-meal"></div>
</div>
<div id="result-heading"></div>
<div id="meals" class="meals">
<div class="meal">
<img class="meal-img" src="https://source.unsplash.com/random" alt="" style="width:180px; height: 180px;border: solid #000 4px;"/>
<div class="meal-info" >
</div>
</div>
const mealsEL = document.getElementById('meals')
mealsEL.addEventListener('click', (e) => {
const mealInfo = e.path.find((item) => {
if (item.classList) {
console.log(item.classList)
return item.classList.contains("meal-info");
} else {
return false;
}
});
// console.log(mealInfo)
if (mealInfo) {
const mealID = mealInfo.getAttribute('data-mealid');
// getMealByID(mealID);
console.log(mealID)
} else {
console.log('no')
}
});
<div id="meals" class="meals">
<div class="meal">
<img class="meal-img" src="" alt="">
<div class="meal-info" data-mealid="75757">
<h3>Click To Show Data Meal Id</h3>
</div>
</div>
<!-- When mealsEL is clicked, the function uses e.path.find to check if the mealsEl children element contain a className of 'meal-info' and stores the result in the variable `const meal-info`
// The second if statement checks if the child element containing className meal-info has an attribute of 'data-mealid'
// Then the value of data-mealid attribute from the child element is logged to the console
</div> -->
// React Code
const getMealByID = (e) => {
const NativePath = e.nativeEvent()
const mealInfo = NativePath.path.find((item) => {
console.log(mealInfo)
if (item.classList) {
return item.classList.contains('meal-info');
} else {
return false;
}
});
if (mealInfo) {
const mealID = mealInfo.getAttribute('data-mealid');
getMealByID(mealID);
}
}
<div id="meals" className="meals" onClick={getMealByID}>
{meals &&
meals.map((meal) => {
const src = meal.strMealThumb;
const mealID = meal.idMeal;
const alt = meal.strMeal;
const index = meal.idMeal;
// const mealIng = meal.strIngredient1;
const mealIng = [];
for (let i = 1; i <= 20; i++) {
if (meal[`strIngredient${i}`]) {
mealIng.push(
`${meal[`strIngredient${i}`]} - ${meal[`strMeasure${i}`]}`
);
} else {
break;
}
}
return (
<div className="meal" key={index}>
<img className="meal-img" src={src} alt="{alt}" />
<div className="meal-info" data-mealid={mealID}>
<h3>{alt}</h3>
</div>
<h2>{mealIng}</h2>
</div>
);
})}

You are getting the error because you are calling getMealByID with an invalid argument, the function expect an event as argument:
if (mealInfo) {
const mealID = mealInfo.getAttribute('data-mealid');
getMealByID(mealID); //--> mealID is not an event
}
Also I think you can get the mealInfo node using less code:
const getMealByID = e => {
const node = e.target;
const mealInfo = node.querySelector(".meal-info");
if (mealInfo) {
const mealID = mealInfo.getAttribute("data-mealid");
...
}
};

// In order to get the data-mealid attribute value from a div
// by using its parent's element . I had to use
// e.target.getAttribute("data-mealid") on a click event from the parents element.
function App() {
const getMealInfoAttribute = e => {
const mealIDData = e.target.getAttribute("data-mealid")
console.log(mealID)
};
return (
<div id="meals" className="meals" onClick={getMealInfoAttribute}>
<div className="meal" key={index}>
<img className="meal-img" src={src} alt="{alt}" />
<div className="meal-info" data-mealid={mealID}>
<h3>{alt}</h3>
</div>
</div>
);
</div>)
}

Related

HTML not responding with JavaScript filter

This is an update to a previous question I had. I am making a simple page that filters the first 9 Pokemon by their type after clicking a button. I will include the code below. I ran the code through a validator and there were no issues. Also, I don't see any errors in the console. I did console the type and the array every time a button is clicked so it looks like the filter is working. But, nothing is happening on the page.
Here's a link to the page: https://sobreratas.github.io/pokemonfilter/
const pokemon=[{name:"Bulbasaur",image:"images/bulbasaur.png",number:1,type:"Grass"},{name:"Ivysaur",image:"images/ivysaur.png",number:2,type:"Grass"},{name:"Venusaur",image:"images/venusaur.png",number:3,type:"Grass"},{name:"Charmander",image:"images/charmander.png",number:4,type:"Fire"},{name:"Charmeleon",image:"images/charmeleon.png",number:5,type:"Fire"},{name:"Charizard",image:"images/charizard.png",number:6,type:"Fire"},{name:"Squirtle",image:"images/squirtle.png",number:7,type:"Water"},{name:"Wartortle",image:"images/wartortle.png",number:8,type:"Water"},{name:"Blastoise",image:"images/blastoise.png",number:9,type:"Water"}];
const sectionCenter = document.querySelector('.section-center');
const btnContainer = document.querySelector('.btn_container');
window.addEventListener('DOMContentLoaded', function() {
displayPokemonMenu();
displayMenuButtons();
})
function displayPokemonMenu(array) {
let pokemonMap = pokemon.map(function(item) {
return `<div class="pokemon-item">
<img class="photo" src="${item.image}" alt="${item.name}">
<h2>${item.name}</h2>
<h3># <span id="number">${item.number}</span></h3>
<h4>Type: <span id="type">${item.type}</span></h4>
</div>`
})
pokemonMap = pokemonMap.join('');
sectionCenter.innerHTML = pokemonMap;
}
function displayMenuButtons() {
let types = pokemon.reduce(function(values, item) {
if (!values.includes(item.type)) {
values.push(item.type);
}
return values
}, ['All']);
const typesBtns = types.map(function(type) {
return `<button class="filter-btn" data-id="${type}">${type}</button>`
}).join('');
btnContainer.innerHTML = typesBtns;
const filterBtns = btnContainer.querySelectorAll('.filter-btn');
filterBtns.forEach(function(btn) {
btn.addEventListener('click', function(e) {
let type = e.currentTarget.dataset.id;
console.log(type)
const pokemonFilter = pokemon.filter(function(item) {
if (type === item.type) {
return item;
}
})
if (type === 'All') {
displayPokemonMenu(pokemon);
} else {
displayPokemonMenu(pokemonFilter);
console.log(pokemonFilter);
}
})
})
}
<img class="logo" src="images/logo.png" alt="logo">
<br>
<br>
<div class="btn_container">
<button class="filter-btn" data-id="All">All</button>
</div>
<div class="section-center">
<div class="pokemon-item">
<img class="photo" src="images/bulbasaur.png" alt="Bulbasaur">
<h2>Bulbasaur</h2>
<h3># <span id="number">1</span></h3>
<h4>Type: <span id="type">Grass</span></h4>
</div>
</div>

Already known weather for city should not repeat again

I'm trying my first weather api APP. Here I'm trying to achive that if the city weather is already displayed , It should give the message "You already know the weather" . and should not repeat the weather
Here is my code. Anyone Please look at my code ...
What is the mistake I have been made.
<div class="main">
<div class="container">
<div class="search_por">
<h2>Weather </h2>
<div class="validate_msg color_white"></div>
<form>
<label for=""></label>
<input type="search" class="input_text" value="">
<button type="submit" id="sub_button" class="srh_button">Search</button>
</form>
<!-- <canvas id="icon1" width="150" height="75"></canvas> -->
<div class="dat_weather">
<ul id="list_it">
</ul>
</div>
</div>
</div>
</div>
var get_text=document.querySelector("form");
get_text.addEventListener("submit",e=>{
e.preventDefault();
var input_val=document.querySelector('input').value;
const apiKey="bc4c7e7826d2178054ee88fe00737da0";
const url=`https://api.openweathermap.org/data/2.5/weather?q=${input_val}&appid=${apiKey}&units=metric`;
fetch(url,{method:'GET'})
.then(response=>response.json())
.then(data=>{console.log(data)
const{main,sys,weather,wind}=data;
//icons-end
var error_ms=document.getElementsByClassName("validate_msg")[0];
var iconcode = weather[0].icon;
console.log(iconcode);
var li=document.createElement("Li");
var weatherinfo=`<div class="nameci font_40" data-name="${data.name},${sys.country}"><span>${data.name}</span><sup>${sys.country}</sup></div>
<div class="temp_ic">
<img class="weat_icon" src="http://openweathermap.org/img/w/${iconcode}.png">
<div class="deg">${Math.floor( main.temp )}<sup>o</sup></div>
</div>
<div class="clear">
<div>${weather[0].description}</div>
</div>
`;
li.innerHTML=weatherinfo;
var ulid=document.getElementById("list_it");
ulid.appendChild(li);
var city_name=data.name;
console.log(skycons);
var listitems=document.querySelectorAll('#list_it');
const listArray=Array.from(listitems);
if(listArray.length>0)
{
var filtered_array=listArray.filter(el=>{
let content="";
if(input_val.includes(','))
{
if(input_val.split(',')[1].length>2)
{
alert("hving 2 commos");
inputval=input_val.split(',')[0];
content=el.querySelector(".nameci span").textContent.toLowerCase();
//content=el.querySelector(".nameci").innerHTML.toLowerCase();
//content=inputval.toLowerCase();
}
else
{
content=el.querySelector(".nameci").dataset.name.toLowerCase();
}
alert(filtered_array);
}
else
{
content=el.querySelector(".nameci span").textContent.toLowerCase();
}
console.log(inputval.toLowerCase());
return inputval.toLowerCase();
});
if(filtered_array.length>0)
{
console.log(filtered_array.length);
error_ms.innerHTML="You Already know the weather of this country....";
get_text.reset();
return;
}
}
})
.catch((error)=>{
error_ms.innerHTML="Please Enter a valid city Name";
});
var error_ms=document.getElementsByClassName("validate_msg")[0];
error_ms.innerHTML="";
//var get_text=document.querySelector("form");
get_text.reset();
});
My full code is here:
https://codepen.io/pavisaran/pen/wvJaqBg
Let's try keeping track of a list of displayed locations outside of the callback:
var get_text = document.querySelector("form");
// Keep Track Of Displayed Cities Here Instead
let displayed = [];
get_text.addEventListener("submit", e => {
e.preventDefault();
var input_val = document.querySelector('input').value;
const apiKey = "bc4c7e7826d2178054ee88fe00737da0";
const url = `https://api.openweathermap.org/data/2.5/weather?q=${input_val}&appid=${apiKey}&units=metric`;
fetch(url, {method: 'GET'})
.then(response => response.json())
.then(data => {
var error_ms = document.getElementsByClassName("validate_msg")[0];
const {main, sys, weather, wind, name} = data;
if (displayed.length > 0) {
// Filter Displayed Based on Current vs name from data (response)
const filtered_array = displayed.filter(el => el === name);
if (filtered_array.length > 0) {
error_ms.innerHTML = "You Already know the weather of this country....";
get_text.reset();
return Promise.resolve();
}
}
// Add City To Array of Displayed Cities
displayed.push(name);
// Do Rest of Code to Add New City
var iconcode = weather[0].icon;
var li = document.createElement("Li");
var weatherinfo = `<div class="nameci font_40" data-name="${data.name},${sys.country}"><span>${data.name}</span><sup>${sys.country}</sup></div>
<div class="temp_ic">
<img class="weat_icon" src="http://openweathermap.org/img/w/${iconcode}.png">
<div class="deg">${Math.floor(main.temp)}<sup>o</sup></div>
</div>
<div class="clear">
<div>${weather[0].description}</div>
</div>
`;
li.innerHTML = weatherinfo;
var ulid = document.getElementById("list_it");
ulid.appendChild(li);
})
.catch((error) => {
error_ms.innerHTML = "Please Enter a valid city Name";
});
var error_ms = document.getElementsByClassName("validate_msg")[0];
error_ms.innerHTML = "";
get_text.reset();
});
You have to just check for the value which is coming from api whether it's present on your list or not. you can try this.
li.innerHTML=weatherinfo;
var ulid=document.getElementById("list_it");
var isPresent = false;
var items = ulid.getElementsByTagName("li");
for (var i = 0; i < items.length; i++){
if(items[i].innerHTML == li.innerHTML){
alert("you already know the weather")
isPresent = true;
}
}
if(!isPresent){
ulid.appendChild(li);
}

queryselector undefined even after checking it with if conditional

I'm learning the basics with Javascript and I'm trying to do a modal that replace an alert, I'm almost done but I have a problem with the querySelector on the button to close it. It returns undefined even if I check it with the if conditional.
function getTemplate(templateName) {
let template = document.querySelector(templateName);
return template.innerHTML;
}
function createFragment(htmlStr) {
let frag = document.createDocumentFragment();
let temp = document.createElement('div');
temp.innerHTML = htmlStr;
while (temp.firstChild) {
frag.appendChild(temp.firstChild);
}
return frag;
}
function putTemplate(template) {
document.body.appendChild(createFragment(template));
}
function openAlert(alertName, btnOpen) {
let openBtn = document.querySelector(btnOpen);
openBtn.addEventListener('click', function () {
putTemplate(getTemplate(alertName));
});
}
function closeAlert(alertName, btnClose) {
let closeBtn = document.querySelector(btnClose);
if (closeBtn) {
closeBtn.addEventListener('click', function () {
let alertWrapper = document.querySelector(alertName);
alertWrapper.parentNode.removeChild(alertWrapper);
});
}
}
function Alert(alertName, btnOpen, btnClose) {
openAlert(alertName, btnOpen);
closeAlert(alertName, btnClose);
}
Alert('#alertTemplate', '.activeBtn', '.deactive');
And this is the markup:
<template id="alertTemplate">
<div id="alertWrapper">
<h1></h1>
<div class="alertBox confirmAlert" role="alert">
<p></p>
<button class="closeBtn deactive troll"></button>
<button class="acceptBtn deactive"></button>
</div>
</div>
</template>
``

How do I give React methods to the onClick handler of an html component?

I'm trying to change the HTML received from a database to respond to custom onClick handlers. Specifically, the HTML I pull has divs called yui-navsets which contain yui_nav page selectors and yui_content page contents. I want to click an li in yui_nav, set that li's class to "selected", set the existing content to display:none, and set the new content to style="".
To do this, I have created a function updateTabs which inputs the index of the chosen yui and the new page number, set that li's class to "selected", set the existing content to display:none, and set the new content to style="". This function works: I tried running updateTabs(2, 3) in componentDidUpdate, and it worked fine, changing the content as requested. I want to assign updateTabs to each of the lis, and I attempt to do so in my componentDidMount after my axios request.
However, I keep getting the error: TypeError: this.updateTabs is not a function. Please help?
Page.js:
import React, { Component } from 'react';
import axios from 'axios';
class Page extends Component {
constructor(props) {
super(props);
this.state = {
innerHTML: "",
pageTags: [],
};
console.log(this.props.url);
}
componentDidMount() {
console.log(this.props.url);
axios
.get(
this.props.db_address + "pages?url=" + this.props.url,
{headers: {"Access-Control-Allow-Origin": "*"}}
)
.then(response => {
this.setState({
innerHTML: response.data[0].html,
pageTags: response.data[1]
});
console.log(response);
// Check for yui boxes, evade the null scenario
var yui_sets = document.getElementsByClassName('yui-navset');
if (yui_sets !== null) {
let yui_set, yui_nav, yui_content;
// Iterate through the navs of each set to find the active tabs
for (var yui_set_count = 0; yui_set_count < yui_sets.length; yui_set_count ++) {
yui_set = yui_sets[yui_set_count];
yui_nav = yui_set.getElementsByClassName('yui-nav')[0].children;
yui_content = yui_set.getElementsByClassName('yui-content')[0].children;
let tab_count;
// Give each nav and tab and appropriate ID for testing purposes
for (tab_count = 0; tab_count < yui_nav.length; tab_count ++) {
yui_nav[tab_count].onclick = function() { this.updateTabs(yui_set_count); }
yui_nav[tab_count].id = "nav-"+ yui_set_count.toString() + "-" + tab_count.toString()
yui_content[tab_count].id = "content-"+ yui_set_count.toString() + "-" + tab_count.toString()
}
}
}
})
.catch(error => {
this.setState({ innerHTML: "ERROR 404: Page not found." })
console.log(error);
});
}
updateTabs(yui_index, tab_index){
// Get all yuis
var yui_sets = document.getElementsByClassName('yui-navset');
let yui_set, yui_nav, yui_content
yui_set = yui_sets[yui_index];
yui_nav = yui_set.getElementsByClassName('yui-nav')[0].children;
yui_content = yui_set.getElementsByClassName('yui-content')[0].children;
// Identify the current active tab
var current_tab_found = false;
var old_index = -1;
while (current_tab_found == false) {
old_index += 1;
if (yui_nav[old_index].className === "selected") {
current_tab_found = true;
}
}
// Identify the new and old navs and contents
var yui_nav_old = yui_nav[old_index]
var yui_nav_new = yui_nav[tab_index]
var yui_content_old = yui_content[old_index]
var yui_content_new = yui_content[tab_index]
// Give the new and old navs and contents their appropriate attributes
yui_nav_old.className = "";
yui_nav_new.className = "selected";
yui_content_old.style = "display:none";
yui_content_new.style = "";
}
render() {
return (
<div className="Page">
<div className="Page-html col-12" dangerouslySetInnerHTML={{__html:this.state.innerHTML}} />
<div className="Page-footer">
<div className="d-flex flex-wrap btn btn-secondary justify-content-around">
{this.state.pageTags.map(function(pageTag){return(
<div className="pd-2" key={pageTag.id}>
{pageTag.name}
</div>
)})}
</div>
<div className="d-flex justify-content-center" >
<div className="p-2">Discuss</div>
<div className="p-2">Rate</div>
<div className="p-2">Edit</div>
</div>
<div className="d-flex justify-content-around App">
<div className="p-2">
Unless otherwise stated, the content
of this page is licensed under <br />
<a href="http://creativecommons.org/licenses/by-sa/3.0/"
target="_blank" rel="noopener noreferrer">
Creative Commons Attribution-ShareAlike 3.0 License
</a>
</div>
</div>
</div>
</div>
)
}
}
export default Page
Instead of function with function keyword use arrow functions and it will be solved as follows
You have
yui_nav[tab_count].onclick = function() { this.updateTabs(yui_set_count); }
But use
yui_nav[tab_count].onclick = () => { this.updateTabs(yui_set_count); }
Use this in componentDidMount method
You have to bind the updateTabs method in the constructor:
constructor(props) {
super(props);
...
this.updateTabs = this.updateTabs.bind(this);
}
You should use arrow functions in order to call this method with the correct contetxt:
yui_nav[tab_count].onclick = () => { this.updateTabs(yui_set_count); }

How to bind onclick handlers to `this` properly on React

Explanation to why this is not a duplicate: My code is already working, I have included as a comment. The question is why the this context change when I include it to click handler function.
I'm attempting a calculator project in React. The goal is to attach onclick handlers to number buttons so the numbers are displayed on the calculator display area. If the handler is written directly to render method it is working, however, if I'm trying from the ComponentDidMount I get an error this.inputDigit is not a function. How do I bind this.inputDigit(digit) properly?
import React from 'react';
import './App.css';
export default class Calculator extends React.Component {
// display of calculator initially zero
state = {
displayValue: '0'
}
//click handler function
inputDigit(digit){
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
componentDidMount(){
//Get all number keys and attach click handler function
var numberKeys = document.getElementsByClassName("number-keys");
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber); // This is not working
};
for (var i = 0; i < numberKeys.length; i++) {
numberKeys[i].onclick = myFunction;
}
}
render() {
const { displayValue } = this.state;
return (
<div className="calculator">
<div className="calculator-display">{displayValue}</div>
<div className="calculator-keypad">
<div className="input-keys">
<div className="digit-keys">
{/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}}
<button className="number-keys">0</button>
<button className="number-keys1">1</button>
<button className="number-keys">2</button>
<button className="number-keys">3</button>
<button className="number-keys">4</button>
<button className="number-keys">5</button>
<button className="number-keys">6</button>
<button className="number-keys">7</button>
<button className="number-keys">8</button>
<button className="number-keys">9</button>
</div>
</div>
</div>
</div>
)
}
}
Thats because you are writing it inside a function which is not bound,
Use
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber);
}.bind(this);
or
const myFunction = () => {
var targetNumber = Number(this.innerHTML);
return this.inputDigit(targetNumber);
}
After this you need to bind the inputDigit function as well since it also uses setState
//click handler function
inputDigit = (digit) => {
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
Since you want to use the button text as well, in that case you should use a separate variable in place of this to call the inputDigit function like
class Calculator extends React.Component {
// display of calculator initially zero
state = {
displayValue: '0'
}
//click handler function
inputDigit(digit){
const { displayValue } = this.state;
this.setState({
displayValue: displayValue+String(digit)
})
}
componentDidMount(){
//Get all number keys and attach click handler function
var numberKeys = document.getElementsByClassName("number-keys");
var that = this;
var myFunction = function() {
var targetNumber = Number(this.innerHTML);
console.log(targetNumber);
return that.inputDigit(targetNumber); // This is not working
};
for (var i = 0; i < numberKeys.length; i++) {
numberKeys[i].onclick = myFunction;
}
}
render() {
const { displayValue } = this.state;
return (
<div className="calculator">
<div className="calculator-display">{displayValue}</div>
<div className="calculator-keypad">
<div className="input-keys">
<div className="digit-keys">
{/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}
<button className="number-keys">0</button>
<button className="number-keys">1</button>
<button className="number-keys">2</button>
<button className="number-keys">3</button>
<button className="number-keys">4</button>
<button className="number-keys">5</button>
<button className="number-keys">6</button>
<button className="number-keys">7</button>
<button className="number-keys">8</button>
<button className="number-keys">9</button>
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<Calculator/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Bind it in the constructor
constructor(props) {
super(props);
this.inputDigit = this.inputDigit.bind(this);
}

Categories