I found this code as the answer of a question:
function getNodeIndex(elm){
return [...elm.parentNode.children].indexOf(elm)
}
I made something so when you click on the document, it logs the target of the click;
If the target is myClass, I want it logs the index of it.
The myClass elements are buttons, that are added when the user clicks on a button.
document.addEventListener("click", function(e) {
if(e.target.classList.value == "myClass") {
console.log(getNodeIndex(e.target))
}
})
But, that's weird:
Even if we click on 1st button, 4th button or 35th button, it will always log 2.
What's the problem there?
Thanks.
Edit:
The full code is here: http://pasted.co/6e55109a
And it is executable on http://zombs.io/
It's due to the structure of your DOM which probably looks something like
<div>
<div>Some Text: <button>Button 1</button></div>
<div>Some Text: <button>Button 2</button></div>
<div>Some Text: <button>Button 3</button></div>
</div>
Each of those buttons is the second child of its parent, i.e. one of the inner divs
Here's how to modify getNodeIndex to get it to work with DOM in this shape. If this still doesn't work, post your DOM.
function getNodeIndex(elm) {
return [...elm.parentNode.parentNode.children].indexOf(elm.parentNode)
}
$('button').on('click', e => {
console.log(getNodeIndex(e.target))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div>Some Text: <button>Button 1</button></div>
<div>Some Text: <button>Button 2</button></div>
<div>Some Text: <button>Button 3</button></div>
</div>
I don't know if your actual code is exactly the same, but the code you posted has 2 errors, you're missing 2 brackets:
/*
Yours:
document.addEventListener("click", function(e) {
if(e.target.classList.value == "myClass") {
console.log(getNodeIndex(e.target)
}
}
*/
// With the brackets
document.addEventListener("click", function(e) {
if(e.target.classList.value == "myClass") {
console.log(getNodeIndex(e.target))
}
})
I've run your code in jsbin and I had no problem, check the brackets and if you´re still having a problem please post your html
You are adding your eventListener to the document. Then you are checking for the classList. The classList may have another class and break your code. You should use classList.contains('some-class') instead.
I would add the click events directly to the 'some-class'-items you want them to be trigger by. This should work as long as you don't add more items to the DOM later. If you do, make sure to add the eventListener too.
// wait for all the html is loaded
window.addEventListener('load', () => {
// get all buttons with the .some-class
const someClassElements = document.querySelectorAll('.some-class');
// iterate over the 'array like' elements
someClassElements.forEach( element => {
// add a click event to the someClassElements
element.addEventListener('click', () => {
// log the nodeIndex
const nodeIndex = getNodeIndex(element);
console.log( nodeIndex );
});
});
}); // end on load
function getNodeIndex(elm){
return [...elm.parentNode.children].indexOf(elm)
}
div{
margin-top: 50px;
}
<div>
<button class='some-class'>some class (0)</button>
<button class='some-class'>some class (1)</button>
<button class='some-class'>some class (2)</button>
<button>no class (3)</button>
<button class='some-class'>some class (4)</button>
<button class='some-class'>some class (5)</button>
<button>no class (6)</button>
<button class='some-class'>some class (7)</button>
</div>
<div>
<button>no class (0)</button>
<button class='some-class'>some class (1)</button>
<button>no class (2)</button>
<button class='some-class'>some class (3)</button>
<button class='some-class'>some class (4)</button>
<button>no class (5)</button>
<button class='some-class'>some class (6)</button>
</div>
The actual answer in your situation:
It seems like you want to know the index of the parent div, not the actual element.
use
console.log(getNodeIndex(e.target.parentNode));
instead of
console.log(getNodeIndex(e.target));
PS: You are always getting 2 as result without your html, you may actually always be clicking the third child of the parent element. I verified that this is the case from your code.
PSII: an extra.. In the code you linked you removed a parent node of an element. Later you try to use that element to make some unsuccessful console.log's, which won't work because you just removed the element.
Related
as I said in title I have problem with HTML elements created with Element.insertAdjacentHTML() method, I'm trying about an hour to solve this but can't. I have button that create new HTML elements, couple of that elements is new buttons with same class or id, it's no matter, that I need to catch in some variable and than again use for event listener, for some reason the class or id for these new created button doesn't exist, is there any way to catch it and use it later, I need Vanila Javascript?
There is over 500 lines of code, this is only for mentioned method
btnClaim.addEventListener("click", () => {
rewardCurrent.style.display = "none";
claimedRewards.push(currentReward);
rewardsList.innerHTML = ``;
claimedRewards.forEach(function (rew, i) {
const html = `
<div class="reward" id="${i}">
<div class="img-text-cont">
<img src="${rew.imgUrl}" alt="">
<div class="text-cont">
<p class="claimed-reward-title">${rew.title}</p>
<p class="claimed-reward-price">$${rew.price}</p>
</div>
</div>
<div class="claimed-rewards-action">
<button id="btn-sell2">Sell</button>
<button id="btn-ship">Ship</button>
</div>
</div>
`;
rewardsList.insertAdjacentHTML("afterbegin", html);
I need that btn-sell2 and btn-ship buttons in variables.
your element is going to be created and doesn't exist at the time page loads, so js addeventlistener will throw an error. to solve you have 2 ways.
1- use parent node that element will be created inside.
addevenlistener to parent and use
parent.addeventlistener( event, function (event){
if(event.target.classList.contains("childClass") {}
}
2- give addeventlistener when creating the element :
function createElement () {
const elem = -craete elemnt-
elem.addeventlistener(event, function);
}
I have the following html and Javascript. I want to have a set of buttons with a single onclick handler.
<div id="parent1">
<button data-idx="1" data-action="test">Click me</button>
<button data-idx="2" data-action="test">Click me</button>
</div>
<div id="parent2">
<button data-idx="1" data-action="test"><span>Click me</span></button>
<button data-idx="2" data-action="test"><span>Click me</span></button>
</div>
<script>
const parent1 = document.querySelector("#parent1");
const parent2 = document.querySelector("#parent2");
parent1.addEventListener("click", (evt) => {
let obj = evt.target.dataset;
console.log(obj);
});
parent2.addEventListener("click", (evt) => {
let obj = evt.path[1].dataset;
console.log(obj);
});
</script>
The code for parent1 works fine and I see {idx:"1", action:"test"}
The code for parent2 works as well, but only because in this example I know in advance the structure of the innerHtml of the buttons. In practise that is not known, and the hard-coded evt.path[1] is not suitable.
It is the case however that the dataset is always in the immediate child of #parent2
How can I reliably find the dataset? For example, can I get the index relative to #parent of the child that fired the event?
Maybe you should use composedpath instead of path: see event.path is undefined running in Firefox.
In generall you can loop over the path[n] and avoid undefined errors.
Or you can access the different variables by using your above evt.target.dataset[keyname].
I have a Main component with code
changeColor = (color) => {
}
toggle = (e) => {
console.log(e.target)
}
<div>
<EditComponent changeColor={this.changeColor.bind(this)}>
<TextComonent toggle={this.toggle.bind(this)}>
</div>
Edit component is
color = (value) => {
this.props.changeColor(value)
}
<div>
<button value='red' onClick={this.color.bind(this,"red")}>Red</button>
<button value='blue' onClick={this.color.bind(this,"blue")}>Blue</button>
</div>
Text component is
toggle = (e) => {
this.props.toggle(e)
}
<div>
<p class="black-color" onClick={this.toggle.bind(this)}>Text 1</p>
<p class="black-color" onClick={this.toggle.bind(this)}>Text 2</p>
</div>
I will be clicking on Text 1 or Text 2 first and I will get the event inside toggle function. Next I will click the button Red or Blue. Then I want to change the class to either red-color or blue-color for that particular Text that i have clicked before. How can I get the event inside the parent component to find the particular text or is there any other way to to this?
I want to get the event.target inside the Parent component. I got the event object in parent but event.target is null
<div>
<EditComponent changeColor={this.changeColor.bind(this)}>
<TextComonent toggle={this.toggle}>
</div>
try this way dont bind function in parent component and try,you will get the target
You are not using "bind" correctly. You don't need to bind with an anonymous function.
class Hello extends React.Component {
render() {
return (
<div>
<p onClick={(e) => this.toggle(e)}>
Test
</p>
</div>);
}
toggle = (e) => {
console.log(e.target.innerText);
}
}
From the event variable in toggle, you can perform your changes as need be.
I found the exact solution as to add event.persist(); to get the event.target inside parent component.
I want to execute a function when I'm clicking on elements in the dom with a specific class. It just doesn't work, but I'm also receiving any error. This is my
code snippet:
methods: {
initTab: function(){
document.querySelectorAll('.element').onclick = this.nextTab()
}
},
mounted: function () {
this.initTab()
}
I
I want to execute the function every time I click on the element. Would be very thankful if anybody could help me :)
There's very little need (if at all) for document.querySelectorAll() in a Vue app.
In this situation you can take advantage of delegation:
<div #click="onClick">
<!-- Clicks on any element inside this div will be handled -->
</div>
methods: {
onClick(e) {
if (e.target.classList.contains('element')) {
// Handle the click
}
}
}
Add #click="initTab($event)" to the document or template root, that allows you to track every click event on your template, that way you could put your logic to the elements which have only .element class name. If you're using it in a component you could do : <template> <div #click="initTab($event)"> ... </div> </template>
var app = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
nextTab(){
console.log("You clicked on an element with class name =element")
},
initTab(event){
let targetClassNames=event.target.className.split(" ");
targetClassNames.filter(e=>{
if(e==="element"){
this.nextTab();
}
});
}
},
mounted() {
}
})
#app{
height:100px;
display:grid
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app" #click="initTab($event)">
<button class="element">1</button>
<button class="element btn">2</button>
<button class="btn">3</button>
<button class="element btn-primary">4</button>
<button class="btn elementory">5</button>
</div>
You're trying to use general javascript logic within vue. This is not often a good idea.
What I do in such cases is something like this:
<component-name #click="nextTab(tabName)"></component-name>
However, in a v-for loop you can also do something like this:
<ul v-for="tab in tabs">
<li #click="nextTab(tab)">{{tab}}</li>
</ul>
That way in methods you only need:
methods: {
nextTab: function(tab){
// whatever it is you want to do here
}
},
And you won't need mounted at all.
Conclusion: try to avoid repetition by creating components or elements (like li) that repeat - not by trying to add an event-listener to a class.
I have some files that load into my react components, which have HTML code.
As it is now, the pure HTML code renders just fine, however there is some 'hidden' code that appears whenever you click certain buttons in other parts of the application or on the text above (think of it like panels that expand when you click on it).
The HTML is hidden just using the good old <div id="someId" style="display:none">.
Anyway I am trying to get the correct panel to expand upon clicking their respective buttons.
So in theory, what I need to do is find the element by id, and switch it's display to block whenever needed, and then switch it back when the parent is clicked again.
Unfortunately I have no idea how to do this and so far have gotten nowhere. As it is now, I have access to the component's ids. What I want to know is how in the world can I access that and get to change whatever is rendering?
Create your function:
function element_do(my_element, what_to_do) {
document.getElementById(my_element).style.display = what_to_do;
}
and latter in code you can append wherever you want through javascript onclick or not depends what do you need:
element_do("someId", "none"); // to hide
element_do("someId", "block"); // to show
or create yourself toggle:
function toggle_element(element_id) {
var element = document.getElementById(element_id);
element.style.display = (element.style.display != 'none' ? 'none' : 'block' );
}
// and you can just call it
<button onClick="toggle_element('some_id')">toggle some element</button>
The react way to do it would be with states. Assuming that you know how to use states I'd do something like this:
class ShowHide extends React.Component {
constructor() {
super();
this.state = {myState: true};
this.onClick = this.onClick.bind(this)
}
onClick() {
this.setState({myState: !this.state.myState}) //set the opposite of true/false
}
render() {
const style = {myState ? "display: none" : "display:block"} //if myState is true/false it will set the style
return (<div>
<button onClick={this.onClick}>Click me to hide/show me </button>
<div id="myDiv" style={style}> Here you will hide/show div on click </div>
</div>)
}
}