why doesn't the gift just get the div id?
clicking modal is returning the whole div instead of just the id, why is that?
jsfidle
function Modals(id, bt, show) {
const modal = document.getElementById(id)
modal.classList.add(show)
modal.addEventListener("click", (elemento) => {
if (elemento.target.id === modal) {
modal.classList.remove(show)
console.log(modal)
}
})
}
const comentarios = document.querySelector(".bt_comentarios")
comentarios.addEventListener("click", () => Modals("modal_comentarios",
"bt_comentarios", "show"))
<div id="modal_comentarios" class="modal fix"></div>
document.getElementById will return an instance of HTMLElement. To get the id of the element, you'll need to use the id property.
const main = document.getElementById('main');
console.log(main);
console.log(main.id);
<div id="main">
</div>
Using your snippet
function Modals(id, bt, show) {
const modal = document.getElementById(id)
modal.classList.add(show)
modal.addEventListener("click", (elemento) => {
// EDIT: I used modal.id
if (elemento.target.id === modal.id) {
modal.classList.remove(show)
// EDIT: I used modal.id
console.log(modal.id)
}
})
}
const comentarios = document.querySelector(".bt_comentarios")
comentarios.addEventListener("click", () => Modals("modal_comentarios",
"bt_comentarios", "show"))
<div id="modal_comentarios" class="modal fix">
<button id="modal_comentarios" class="bt_comentarios">Click Me!</button>
</div>
What I changed
I added a few pieces to make the code go down the path of your if statement, and referenced the modal.id property instead of the instance of the HTMLElement itself. Let me know if you have more questions.
Related
I want to create a button that will hide each ticket and one general button that will restore them all.
this is the Code:
return (
<ul className="tickets">
{filteredTickets.map((ticket) => (
<li key={ticket.id} className="ticket">
<h5 className="headline">{ticket.headline}</h5>
<p className="text">{ticket.text}</p>
<footer>
<div className="data">
By {ticket.address} | {new Date(ticket.time).toLocaleString()}
</div>
</footer>
</li>
))}
</ul>
);
here is an example of what you want!
you have to replace myFunction() for your button and myDIV into your element that you want to hide it!
<button onclick="myFunction()">Click Me</button>
function myFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
for react =
const [visible, setVisible] = useState(true)
here is for button
<button onlick={() =>setVisible(!visible)}>hide/show
here is a demo in JS, modify to what you want exactly
<ul class="ticket">
<li>
<p>hey, I'm a P</p>
<div class="data">I'm a Div</div>
</li>
</ul>
.hide {display:none}
const generalBtn = document.getElementById(`btn`);
const divContainer = document.querySelector(`.ticket`);
const eachDiv = divContainer.getElementsByClassName(`data`);
generalBtn.addEventListener(`click`, () => {
[...eachDiv].forEach((div) => {
div.classList.toggle(`hide`);
});
});
There is a good solution in your case but as mentioned in the comments, it needs to manipulate the filteredTickets array.
You need to add a property/value to each item of filteredTickets to track or change their state. For example, it can be isVisible property which is a boolean with false or true value.
Now, isVisible value will determine the behavior. let's modify the ticket:
const handleHideTicket = (id) => {
// find selected ticket and change its visibility
const updatedFilterdTickets = filteredTikcets.map(ticket => (ticket.id === id ? {...ticket, isVisible: false} : ticket))
// now the updatedFilterdTickets need to be set in your state or general state like redux or you need to send it to the server throw a API calling.
}
return (
<ul className="tickets">
{filteredTickets.filter(ticket => ticket.isVisible).map((ticket) => (
<li key={ticket.id} className="ticket">
<h5 className="headline">{ticket.headline}</h5>
<p className="text">{ticket.text}</p>
<footer>
<div className="data">
By {ticket.address} | {new Date(ticket.time).toLocaleString()}
</div>
// add a button to control visibility of each ticket
<button onClick={() => handleHideTicket (ticket.id)}> click to hid / show </button>
</footer>
</li>
))}
</ul>
);
Explanation:
a new button added to each ticket and pass the handleHideTicket handler to it. If the user clicks on this button, the handler finds that ticket and sets the isVisible property to the false.
On the other hand, we can remove the hidden tickets by applying a simple filter method before map method. so only visible tickets will be displayed.
Now, create a general button to show all the tickets. In this case, you need a handler function that sets all ticket's isVisible value to true
const handleShowAllTickets = () => {
const updatedFilteredTickets = filteredTickets.map(ticket => ({...ticket, isVisible: true}))
// now put the updatedFilteredTickets in your general store/post an API call/ update state
}
Note: as I mentioned in the code's comments, you need to update your filteredTickets array after changing via handlers to reflect the changes in your elements.
I need to render a new card that already has a function with the change event. I was trying to remove a class from the actual card (show) so it wont show the card and then add a class (show) to the oter card that I want to render. I did this code, but it doesnt work, any other ideas? thanks.
const dropdownmenucli = () => {
const print = document.getElementById ('drop2');
const dropdown= document.getElementById ('dropdowns2');
const printDocument=document.getElementById('cnd');
const delWindow=document.getElementById('exampleModal2');
const popWindow=document.getElementById('exampleModal');
dropdown.addEventListener('change', (event) => {
const value = document.getElementById('dropdowns2').value;
print.innerHTML=`
<div class="padding-b" >
<a class="text-decoration btn btn-primary" id="cnd"> Create New Document </a>
<a class="text-decoration btn btn-primary" href="/Create/${value}"> Existing Document</a>
</div>
`
})
}
const Redirect = document.getElementById('cnd') ? () => {
printDocument.addEventListener('clik',(event) => {
delWindow.classList.remove("show");
popWindow.classList.add("show");
})
}: null
dropdownmenucli()
I have an array of data from which I am creating elements. And when I hang the event handler, then the dataset.container is undefined. How do I get to the attributes, or how do I create elements differently to get the data-category?
const cards = [
[],
[],
[]
]
function renderCategory(card) {
let str = '';
for (let i = 0; i < card.length; i++) {
str += `<div class="card card-category" data-category="${i}">
<div class="card-image">
<img src="${card[i].image}">
</div>
<div class="card-desc">
<div class="card-text">${card[i].name}</div>
</div>
</div>`;
}
return str;
}
let categoryCard = cards[0];
function createCard(cards) {
const main = document.getElementById('category');
main.innerHTML += renderCategory(cards);
}
createCard(categoryCard)
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#category').addEventListener('click', ({
target: {
dataset
}
}) => {
console.log(dataset.category) // undefined
})
})
So several things
For one, I could not recognise your addEventListener('click', ({ target: { dataset } construction as valid JavaScript.
I have tried to not change a lot but I
return complete set of cards
delegate from container correctly - clicking anywhere in the card will return the category
let categoryCard;
const cards = [
{ image : "https://via.placeholder.com/128x90.png?text=Card1", name:"Card 1"},
{ image : "https://via.placeholder.com/128x90.png?text=Card2", name:"Card 2"},
{ image : "https://via.placeholder.com/128x90.png?text=Card3", name:"Card 3"}
]
function renderCategory(cards) {
return cards.map((card,i) => (
`<div class="card card-category" data-category="${i}">
<div class="card-image">
<img src="${card.image}">
</div>
<div class="card-desc">
<div class="card-text">${card.name}</div>
</div>
</div>`)).join("");
}
function createCard(cards) {
const main = document.getElementById('category');
main.innerHTML += renderCategory(cards);
categoryCard = main.querySelector(".card-category"); // first card
}
window.addEventListener('load', () => {
document.querySelector('#category').addEventListener('click', e => {
const tgt = e.target;
const parent = tgt.classList.contains("card-category") ? tgt : tgt.closest(".card-category")
console.log(parent && parent.dataset.category ? parent.dataset.category : "clicked somewhere else")
})
createCard(cards)
})
<div id="category"></div>
event.target refers to the lowest Element of the DOM tree on which the event was triggered. In other words, depending on where you click the target can be the card-category DIV, the card-image DIV, the card-desc DIV, the card-text DIV or the IMG element. That's why you're not able to access the data-attribute which is only set on the parent.
If you want to always get the Element to which you bound the event, use event.currentTarget instead. But in your case, you bound the event to the main #category wrapper. If you want to bind the event to the element that has the data-category attribute, you'll have to reorganize your code to use document.createElement instead of producing HTML strings.
Keep in mind you should append elements to the page before binding events to them.
I wrote toggle script in ES6/vanilla JS. The intended functionality is super simple, you click on the toggle div and it adds an active class to another div that matches the toggle div's data-toggle property. In my toggle div, I need there to be both text and an image. It works great when you click on the text within the div, but when you click on the image within the div, the toggle is not firing. Is there something specific I need to do to include all of the children within the div?
For some reason, I can't even get this working via this code snippet editor, but it is working in my project.
const setActive = (toggles, panels, id) => {
let activePanel = panels.filter(panel => panel.getAttribute('data-toggle') == id)
let activeToggle = toggles.filter(toggle => toggle.getAttribute('data-toggle') == id)
activePanel.forEach(panel => panel.classList.add('active'))
activeToggle.forEach(toggle => toggle.classList.add('active'))
}
const removeActive = (nodes) => {
nodes.forEach(node => node.classList.remove('active'))
}
const handler = (event) => {
event.preventDefault()
let id = event.target.getAttribute('data-toggle')
let panels = Array(...document.querySelectorAll('.js-toggle-panel'))
let toggles = Array(...document.querySelectorAll('.js-toggle'))
removeActive(panels)
removeActive(toggles)
setActive(toggles, panels, id)
}
let toggles = Array(...document.querySelectorAll('.js-toggle'))
toggles.forEach(toggle => toggle.addEventListener('click', handler))
.toggle-panel {
display: none;
}
.toggle-panel .active {
display: block;
}
<div class="js-toggle toggle" data-toggle="toggle-1">
<img src="https://via.placeholder.com/50"> First toggle
</div>
<div class="js-toggle toggle" data-toggle="toggle-2">
Second toggle
</div>
<div class="js-toggle-panel toggle-panel" data-toggle="toggle-1">
<h1>Toggle 1</h1>
</div>
<div class="js-toggle-panel toggle-panel" data-toggle="toggle-2">
<h1>Second toggle!</h1>
</div>
I changed two things that I believe will resolve your issue:
I changed the selector .toggle-panel .active to .toggle-panel.active-- without that, even in the cases where the JS was working as you intended nothing was actually be made visible.
I moved your code from using event.target to event.currentTarget -- the former always points to the clicked element, whereas the latter refers to the element on which the listener has been placed.
See the snippet below.
const setActive = (toggles, panels, id) => {
let activePanel = panels.filter(panel => panel.getAttribute('data-toggle') == id)
let activeToggle = toggles.filter(toggle => toggle.getAttribute('data-toggle') == id)
activePanel.forEach(panel => panel.classList.add('active'))
activeToggle.forEach(toggle => toggle.classList.add('active'))
}
const removeActive = (nodes) => {
nodes.forEach(node => node.classList.remove('active'))
}
const handler = (event) => {
event.preventDefault()
let id = event.currentTarget.getAttribute('data-toggle')
let panels = Array(...document.querySelectorAll('.js-toggle-panel'))
let toggles = Array(...document.querySelectorAll('.js-toggle'))
removeActive(panels)
removeActive(toggles)
setActive(toggles, panels, id)
}
let toggles = Array(...document.querySelectorAll('.js-toggle'))
toggles.forEach(toggle => toggle.addEventListener('click', handler))
.toggle-panel {
display: none;
}
.toggle-panel.active {
display: block;
}
<div class="js-toggle toggle" data-toggle="toggle-1">
<img src="https://via.placeholder.com/50"> First toggle
</div>
<div class="js-toggle toggle" data-toggle="toggle-2">
Second toggle
</div>
<div class="js-toggle-panel toggle-panel" data-toggle="toggle-1">
<h1>Toggle 1</h1>
</div>
<div class="js-toggle-panel toggle-panel" data-toggle="toggle-2">
<h1>Second toggle!</h1>
</div>
Instead of event.target you should use event.currentTarget in your handler function to return node to which event listener is attached. event.target is returning <img> node, not <div> with data-toggle in your case.
I'm currently trying to make a recursive function that takes html elements as an array so I can take html elements like the querySelector function
The reason i'm doing this is because I can't use getElementsByTagName() or querySelector()
Here is my code:
function flatten(items)
{
const flat = [];
items.forEach(item => {
if (Array.isArray(item)) {
flat.push(...flatten(item));
}
else {
flat.push(item);
}
});
return flat;
}
var button = flatten(footer).flatten(div);
count = 0;
button.onclick = function() {
count += 1;
button.innerHTML = count;
};
I get the following error: ReferenceError: footer is not defined
Thanks
Here is my HTML code:
<div class="wrapper">
<div class="container">
<footer>
<div>
</div>
</footer>
</div>
</div>
Edit:
footer is defined in my HTML, I want to select footer in my function
Also, I can't add class or id to my html, I can't edit it
If, for the sake of practice (or a lost bet), you'd want to write your own querySelectorAll, you could write a recursive function that walks the DOM tree... The only thing you rely on is an entrance to the DOM: window.document.
Note that this will never be able to compete with the performance of your browser's default query implementations. We're just doing it to show we can.
Step 1: recursively walking the document (depth-first)
const walk = (el) => {
console.log(el.nodeName);
Array.from(el.children).forEach(walk);
};
walk(document);
<div class="wrapper">
<div class="container">
<footer>
<div>
</div>
</footer>
</div>
</div>
As you can see, this function loops over each element in the document and its children.
Step 2: Adding the filter logic
If you want it to actually find and return elements, you'll have to pass some sort of filtering logic. querySelectorAll works with string inputs, which you could try to recreate... Since we're redoing this for fun, our select will work with functions of HTMLElement -> bool.
const selectIn = (pred, el, result = []) => {
if (pred(el)) result.push(el);
Array.from(el.children)
.filter(e => e)
.map(el2 => selectIn(pred, el2, result));
return result;
}
// EXAMPLE APP
// Define some selectors
const withClass = className => el =>
el && el.classList && el.classList.contains(className);
const withTag = tagName => el =>
el && el.nodeName === tagName.toUpperCase();
// Select some elements
const footer = selectIn(withTag("footer"), document)[0];
const container = selectIn(withClass("container"), document)[0];
const divsInFooter = selectIn(withTag("div"), footer);
// Log the results
console.log(`
footer:
${footer.outerHTML}
container:
${container.outerHTML}
divsInFooter:
${divsInFooter.map(d => d.outerHTML)}
`);
<div class="wrapper"><div class="container"><footer><div></div></footer></div></div>