Detect child node number - javascript

I'm trying to add a event listener for clicking and I want to know the position of the node that was clicked
function evl(etna) {
document.addEventListener("click", function (el) {
alert("You clicked on " + 'the name of element that was clicked or his array code');
}, false);
};
where etna is:
document.getElementsByTagName("*");

function evl(etna){
document.addEventListener("click",function (el) {
var clickedElement = el.target || el.srcElement;
alert("Link detected a click. Cancellable: "+clickedElement.name);
for(var i = 0; i < etna.length; i++) {
if(etna[i] === clickedElement) {
//i is a position of an element in etna
break;
}
}
},false);
};
You can use this which will point to a clicked element. As to Phil H IE 8 does not work that way. But anyway, there should be used .target or .srcElement. And maybe it will be better to get its id. Name attribute is not valid for divs, spans, etc.
But also you are attaching an event to a document. And this will point to a document.
Instead of that you should use el.target || el.srcElement where .target/.srcElement is a pointer to a node where click actually happened.
Also, I do not think you can get index of an element in array (actually, node list) returned by document.getElementsByTagName("*") (well, you can get that list and iterate through it in loop and check each element if it is eaqual to this). Plus, I have no idea why it could be needed.

Add a loop and set the event listener differently for each item in the etna array:
function evl(etna){
for(var i=0; i < etna.length; ++i) {
(function() {
var thisNode = etna[i];
var localval = i;
etna[i].addEventListener("click",function (el) {
alert("Link detected a click. Cancellable: "+ thisNode.id + " which is id " + localval );
},false);
})();
}
}
Working jsfiddle: http://jsfiddle.net/5xDjE/
The function that is immediately called is merely to force the scoping of thisNode and localval, otherwise all the elements get references to the same variable (javascript scoping is interesting).
I would advise against using the index (scoped via localval) because it requires retaining the original array of nodes. Since nodes change over time and javascript does reference counting for you, you want to avoid these kinds of long arrays of nodes.
Note that this doesn't always have element that was clicked, in IE8 and below this points to the global window object.

Related

remove element javascript with classename

when I remove this with id it works but when I tried with className it didn't work
const remov= document.createElement('a');
remov.classList='clsx';
remov.textContent='X';
remov.setAttribute("id", "Div1");
remov.onclick = function (){
document.getElementById('Div1').remove(); }
this is my code im trying to remove the element that i've created with id 'Div1' emphasized text
const lista = document.getElementById('list');
eventlisteners();
function eventlisteners(){
document.querySelector('#form').addEventListener('submit', yrlist);
}
//function
function yrlist(e){
e.preventDefault();
var textare =document.getElementById('textar').value;
const li= document.createElement('li');
li.textContent=textare;
li.classList='clsli';
li.setAttribute("id", "Div1");
const remov= document.createElement('a');
remov.classList='clsx';
remov.textContent='X';
remov.setAttribute("id", "Div1");
remov.onclick = function (){
document.getElementById('Div1').remove();
}
li.appendChild(remov);
lista.appendChild(li);
}
Note that getElementById returns single element while getElementsByClassName returns list of elements. So you need to iterate through this list. And remove each element.
For example you can do this using loop:
const elements = document.getElementsByClassName("clsx");
for(var x=0; x < elements.length; x++) {
elements[x].remove();
}
remov.classList = 'clasx'; is just wrong. The classList member is actually a DOMTokenList, not a string. As such, it has functions of it's own. Rather than trying to set this member, you need to use the add function it contains.
I.e
remov.classList.add('clsx');
It seems that clicking on the element will have the effect of removing it. This is functionality you've given it at creation time. There's a better way to go about this. Rather than trying to find the element, (which must be identifiable in some manner) why not just have the element remove itself? If you connect the action and the event using AddEventListener, rather than by overwriting a member of the element yourself, a neat thing happens - the function that gets called behaves as though it was a part of the element that triggered it, and so the this keyword refers to itself. It's a huge help, but for the uninitiated, can be a real headache.
const removeMe = document.createElement('a');
removeMe.classList.add('clsx');
removeMe.textContent = 'X';
removeMe.addEventListener('click', function(evt){ this.remove(); }, false);
li.appendChild(removeMe);

For loop in Javascript/Jquery

I am making use of Gridster widgets on webpage.Each widget is having a button on it which changes the color to red of widget after that is clicked(It basically should imply that this widget is selected).Once that button is pressed the parent element(of the button clicked that is a particular widget) gets added to an array as well
The meta information about variables in for loop is as follows
parentElement:An array which should have widgets which are highlighted/selected
currentElement: The widget which is currently selected or for which the button is pressed
I have written a for loop and the expected logic of that loop should be as follows
If the array parentElement is empty (That is no element in the array)
then the element which is clicked currentElement should get added in the array
If the array parentElement is not empty then the element which is clicked currentElement should be checked for its presence in the entire parentElement array
If currentElement found then currentElement should not be added in parentElement else it should be added
My main aim is to remove the currentElement from parentElement array if it is already present in the parentElement array
My JS for same is as follows
var parentElement = [];
$(document).on("click", ".change-widget-color", function() {
currentElement = $(this).closest('li');
$(this).closest('li').toggleClass("red") //Highlighting the element which is selected
if (parentElement.length === 0) {
parentElement.push(currentElement);
}
else {
for (var i = 0; i < parentElement.length; i++) {
if (JSON.stringify(parentElement[i]) === JSON.stringify(currentElement)) {
console.log("Element already present");
}
else {
parentElement.push(currentElement);
}
}
}
});
Overall Expected Output
My main intention is that before adding currentElement in the array parentElement it should be checked.If it is present then I should remove that element
I am not able to figure out how I will remove the current element if it is already present.
The Problem in my for loop
The above for loop behaves in a very wierd way.I dont know whats wrong with the logic but when I do console.log it executes both if and else condition
Any help will be really appreciated
Fiddle
You are modifying the array as you loop through it.
So, in one loop, you are adding the currentElement to the array. This increments parentElement.length. Thus, you do another iteration of the loop where the index points to the currentElement that you just inserted.
EDIT: Also you are calling JSON.stringify() on a jquery element, which is not what you want. You should just do a direct compare on the element. Notice the [0] part which takes the first DOM element out of jquery.
You should check the array to see if it's unique, and then insert it like so:
if (parentElement.every(element => element[0] !== currentElement[0])) {
parentElement.push(currentElement);
}
Base on AnilRedshift's answer, you can remove the same element by the following code:
$(document).on("click", ".select-element", function() {
currentElement = $(this).closest('li');
$(this).closest('li').toggleClass("red") //Highlighting the element which is selected
if (parentElement.length === 0) {
parentElement.push(currentElement);
console.log("Parent Element");
console.log(parentElement);
} else {
// check if the array already has the same element
if (parentElement.every(element => element[0] !== currentElement[0])) {
parentElement.push(currentElement);
}
else{
console.log('Element already present')
// find the index of the existed element
var index = parentElement.findIndex(x=>x[0] === currentElement[0])
// remove it
parentElement.splice(index,1)
}
console.log("Final Parent Element");
console.log(parentElement);
}
});

JavaScript: How to skip over current item in array during a for loop? (continue?)

EDIT: I don't want to skip index 1. I want to skip the current (clicked on) element. Also, see below for more of the code as requested. You'll see that I have a class CatListItem and five instances of that class in an array allCatListItems.
Here's some context for the question: I have a list of cats. When I click on a cat's name (list item), I want that cat's picture and other info to be appended to the page (got that down). When a cat is clicked, I also want any other cat that is being displayed to be hidden (that way there is only one cat on the screen at a time).
I'm trying to accomplish this with a for loop, but obviously if it iterates over every item in the array, then when I click an item, the cat being clicked will be hidden as well.
I want to skip the current item in the array and only run the code on the other items. Using continue, I know I can skip a specific item (item 1 in the below example). This will run my code on every item in the array except that at index one. But how can I make that continue dynamic? Meaning... how can I hide all of the cats, except the one being currently clicked?
Here's the loop that skips index 1:
CatListItem.prototype.hideCats = function() {
allCatListItems.forEach(function(cat) {
cat.a.addEventListener('click', function() {
for (var i = 0; i < allCatListItems.length; i++) {
if (i === 1) {
continue;
}
allCatListItems[i].img.className = 'hide';
};
});
});
}
var allCatListItems = [
catListItem1 = new CatListItem('El', 'images/el.jpg', 'el'),
catListItem2 = new CatListItem('Widdle Baby', 'images/widdle-baby.jpg', 'widdle-baby'),
catListItem3 = new CatListItem('Mama', 'images/mama.jpg', 'mama'),
catListItem4 = new CatListItem('Legion', 'images/legion.jpg', 'legion'),
catListItem5 = new CatListItem('Boy', 'images/boy.jpg', 'boy'),
];
EDIT: Here's a fiddle.JSFIDDLE Click the names to see the functionality without the hideCats function. Then uncomment where it says to to see my issue.
I'm starting to think maybe a for loop isn't the best option?
In that case compare the event.target(its the element clicked)
EDIT: allCatListItems[i] needs it's .a property attached to it in the if statement (this is what contains the anchor element). This is because the event listener is grabbing an anchor tag, so e.target will be returning an anchor tag as well. The if statement will never return as true if you aren't comparing the same type of element.
cat.a.addEventListener('click', function(e) {
for (var i = 0; i < allCatListItems.length; i++) {
if (allCatListItems[i].a === e.target) {
continue;
}
allCatListItems[i].img.className += ' hide';
}
});
Here is a jsfiddle, it doesn't use the same element names, but it should be doing what you want. https://jsfiddle.net/5qb4rwzc/
$('li').on('click', function() {
var index = $(this).index();
var items = document.getElementsByTagName('li');
for(var i = 0; i < items.length; i++) {
if(i === index) continue;
items[i].style = "display:none;";
}
});
Its really depend on how you call the function "hideCat". Realizing that each time that function is called, more eventListeners are add to every cat item. Each time a cat is clicked, more than one event fired. Perhaps you should re-structure how to attach eventListeners to each cat item.

Counting parent nodes

Is there a native method of DOM element in ECMAScript that will allow to count all ancestors of a given element (up to window object or DOM element specified by Id,Name etc.)?
Example use is to check all ancestors of a given element and remove a specified attribute.
There is the node iterator ( https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator ) which could be used for this purpose
You can use xpath:
document.evaluate('ancestor::*', x, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null)
See https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript for more details.
I've wrote a simple function that does an action described in a question. It gets all ancestors of a given element and removes a given attribute from every element that is between "starting" and "ending" element (it does not perform an removeAttribute method on "ending" element).
var modifyAncestors = function(startingelementid,endingelementId,searchedattribute) {
var nodecount = document.getElementById(endingelementId).childNodes.length;
console.log(nodecount);
var currentid = startingelementid;
console.log(currentid);
console.log(searchedattribute);
for(var i = 0; i < nodecount; i++) {
if(currentid == endingelementId) {
break;
}
else {
document.getElementById(currentid).removeAttribute(searchedattribute);
currentid = document.getElementById(currentid).parentNode.id;
}
}
}
Working example: http://www.blacktieseo.com/so/js/test.html (couldn't get it to work with Fiddle JS).
Any comments, bugs etc. will be highly appreciated.

Identifying list item index - which is a better approach?

I need to pick up list items from an list, and then perform operations like adding event handlers on them. I can think of two ways of doing this.
HTML:
<ul id="list">
<li id="listItem-0"> first item </li>
<li id="listItem-1"> second item </li>
<li id="listItem-2"> third item </li>
</ul>
Using the IDs-
for(i=0;i<3;i++)
{
document.getElementById("listItem-"+i).addEventListener("click",foo,false);
}
Using childNodes property-
for(i=0;i<3;i++)
{
document.getElementById("list").childNodes[i]
.addEventListener("click",foo,false);
}
The reason i'm using the first approach is that in the function foo, if I want the index at which the item is located in the list, i can do it by splitting the id -
function foo()
{
tempArr = this.id.split('-');
index = tempArr[tempArr.length-1]; // the last element in the array
}
I can't think of a way of doing it by using the second method, that is, without using the id naming scheme.
The questions:
How do I get the index using the second method or any better method
Are there some very bad affects of following the first method ?
You can avoid adding event handlers to each list item by adding a single event handler to the containing element (the unordered list) and leveraging the concept of event bubbling. In this single event handler, you can use properties of the event object to determine what was clicked.
It appears that you are wanting to map data in an array to list items. While parsing out an array index out of the list item id would work, another approach would be to store a "key" value on the list item as an expando, and use javascript object properties to do a lookup for your data.
Partial Example:
<li key="myKey">
//oData is a object (eg. var oData = {};) that has been populated with properties
//whose value is the desired data (eg. oData["myKey"] = 123;)
alert(oData[event.srcElement.key]); // alerts 123
As far as bad effects of the first technique you showed, one bad effect is that with many list items you end up with many event handlers being defined, which at some point would have an impact on performance.
Also note that you may be unintentionally creating a global var in your loops by omitting the var keyword for "i".
If you opt to use jQuery it comes as simple as:
$('ul#list li').click(function () {
var i = this.id.split('-').pop();
alert( i );
});
Maybe something similar to:
var lis = document.getElementById("list").getElementsByTagName("li");
for (var i = 0, li; li = lis[i]; ++i) {
li.addEventListener("click", (function(pos) {
return function() {
alert(pos);
};
})(i), false);
}
Or, with some inspiration from J cs answer and custom data attributes:
var lis = document.getElementById("list").getElementsByTagName("li");
for (var i = 0, li; li = lis[i]; ++i) {
li.setAttribute("data-index", i); // Or whatever value you want...
li.addEventListener("click", function() {
alert(this.getAttribute("data-index"));
}, false);
}
As stated before, if you want to retrieve the list of li, you should use getElementsByTagName on your ul, because childNodes might retrieve some text nodes as well as li nodes.
Now if what you need is to use the index in the event handler, you might be better directly using a closure in order to reuse the loop variable inside the event handler.
var lis = document.getElementById("list").getElementsByTagName("li");
for( var i = 0, l = lis.length; i < l; ++i )
{
(function(){
// As a new variable will be created for each loop,
// you can use it in your event handler
var li = lis[i];
li.addEventListener("click", function()
{
li.className = "clicked";
}, false);
})();
}
But you may consider event delegation for this purpose. You only have to attach an event handler to the parent element and find the clicked element using the target attribute.
document.getElementById("list").addEventListener("click", function(event)
{
var li = event.target;
if( li.nodeName.toLowerCase() == "li" )
{
...
}
}, false);

Categories

Resources