Attach event to child elements - javascript

<div class="tabContainer">
<ul class="Tabs" id="tabset">
<li id="tab1" class="selected" onclick="tabs(this)">Home</li>
<li id="tab2" onclick="tabs(this)">Tab 2</li>
<li id="tab3" onclick="tabs(this)">Tab 3</li>
</ul>
</div>
I want to remove onclick="tabs(this)" and turn it into an event. How can I do this?
Currently the below code is my solution which isn't working.
var h=$("tabset").children;
for (i=0;i<h.length;i++) h[i].onclick=tabs(this);
Oh and no jquery please, but var $ = function(x){return document.getElementById(x)};
NOTE: I need the (this) part.

You seem to be using some $ function in your code which is not standard. For example if you are using jQuery your code could look like this:
$('#tabset li').on('click', tabs);
If you don't want to use jQuery then you will have to adapt your selector:
var h = document.getElementById('tabset').getElementsByTagName('li');
for (var i = 0; i < h.length; i++) {
h[i].onclick = tabs;
}
or since you declared:
var $ = function(x){
return document.getElementById(x)
};
you could also use:
var h = $('tabset').getElementsByTagName('li');
for (var i = 0; i < h.length; i++) {
h[i].onclick = tabs;
}

I'd suggest:
var lis = document.getElementById('tabset').getElementsByTagName('li');
for (var i = 0, len = lis.length; i < len; i++){
lis[i].onclick = tabs; // inside the function 'this' will refer to the node.
}
JS Fiddle demo.
Though you could assign the click-handler to the containing ul element:
var list = document.getElementById('tabset');
tabset.onclick = list.addEventListener('click', function(e){
tabs(e.target);
}, false);
JS Fiddle demo.
You could, now I see what you're doing with your function, avoid using getElementById() and simply chain:
$('tabset').addEventListener('click', function(e){
tabs(e.target);
}, false);
JS Fiddle demo.
With jQuery, as you appeared (prior to an edit) to be trying to use:
$('#tabset li').click(function(){
tabs(this);
});

Related

JavaScript for loop innerHTML returning as undefined

I have been racking my brain all morning about this. In my HTML code I have a list containing letters a - j and want to console log the letter when it is clicked.
I have created a for loop to run through the nodelist and I can get the script to console log out the number of the element that is being clicked but as soon as I try and get the innerHTML or anything like that it returns undefined.
But when I just type el[3].innerHTML for example into the console it returns the letter I want.
Please help me understand why just because it's going through a for loop does it make it undefined?
I'm not just looking for the solution but also to learn why it's happening so I can become a better coder.
Thanks!
Code:
var list = document.getElementsByTagName('ul')[0];
var el = list.getElementsByTagName('li');
for(var i = 0; i < el.length; i++ ) {
el[i].addEventListener('click', function(f) {
return function(event) {
event.preventDefault();
console.log(f.innerHTML);
}
}(i));
}
You miss name of variable at :
console.log(this.innerHTML);
var list = document.getElementsByTagName('ul')[0];
var el = list.getElementsByTagName('li');
console.log(el)
for(var i = 0; i < el.length; i++ ) {
el[i].addEventListener("click",function(f){
return function(event){
event.preventDefault();
console.log(this.innerHTML);
};
}(i));
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
When you pass i as your argument into the function, f takes on the value of i so when you try to get f.innerHTML you're attempting to get the HTML from an integer, rather than from the element that was clicked.
You can use this to target the element that was clicked. Here is a working example:
var list = document.getElementsByTagName('ul')[0];
var el = list.getElementsByTagName('li');
for(var i = 0; i < el.length; i++ ) {
el[i].addEventListener("click",function(f){
return function(event){
event.preventDefault();
console.log(this.innerHTML);
};
}(i));
}
<ul>List
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
Edited with corrected information thanks to #Teemu.
Here is working example, I think it was problem with passing correct element
var list = document.getElementsByTagName('ul')[0];
var el = list.getElementsByTagName('li');
for(var i = 0; i < el.length; i++ ) {
el[i].addEventListener("click",function(e){
return function(event){
event.preventDefault();
console.log(e.innerHTML);
};
}(el[i]));
}
Your Main Problem
As alredy mentiont in the comments by #teemu your f is the event object. And to access innerHtml you need to access target befor.
Idea
Currently you have n <li> elements an eventListener. You can have only one on your <ul> to have a cleaner code base.
Code
When you add the eventListener to <ul> you can access each clicked element in it via event.target.
var list = document.getElementsByTagName('ul')[0];
list.addEventListener('click', logInnerHtml)
function logInnerHtml(event) {
event.preventDefault()
console.log(
event.currentTarget.innerHtml
)
}
Example
var list = document.getElementsByTagName('ul')[0];
list.addEventListener('click', logInnerHtml)
function logInnerHtml(event) {
console.log(event.target.innerHTML)
}
<ul>
<li>first</li>
<li>second</li>
</ul>

Getting index of element clicked with Array.prototype.indexof.call(); [duplicate]

Lets imagine I have the following HTML code.
I need to find the position within the LI elements for the LI which has the class focus applied.
In this example the result should be 2 (if at 0 index). Any idea how to do it?
<ul class="mylist">
<li>Milk</li>
<li>Tea</li>
<li class="focus">Coffee</li>
</ul>
In pure JavaScript:
var index = 0;
var lis = document.getElementsByTagName('li');
for (var len = lis.length; index < len; ++index) {
if (lis[index].className.match(/\bfocus\b/)) {
break;
}
}
(fiddle)
While you've already accepted an answer to your question, I thought I'd put together a simple index() method for HTMLElements:
HTMLElement.prototype.index = function () {
var self = this,
parent = self.parentNode,
i = 0;
while (self.previousElementSibling){
i++;
self = self.previousElementSibling
}
return this === parent.children[i] ? i : -1;
}
var focus = document.querySelector('li.focus'),
i = focus.index();
console.log(i); // 2
JS Fiddle demo.
References:
Element.previousElementSibling
document.querySelector().
Use .index()
var index = $('.focus').index();
DEMO
Specific list
$('.mylist .focus').index()
DEMO
In jQuery, you should be able to get it, via index. With classes, you could run into issues, when having multiple of them.
I prepared a Plunker, where you can see a solution to the problem.
the expanded version (jquery) would be
$(document).ready(function(){
$('li').each(function(){
var cls=$(this).attr('class');
if(cls=='focus'){
alert($(this).index());
}
});
});

alert() works while elem.classList.add() doesn't add the class at the li element

I want to add a class to every li in #tabs_ul when it is clicked, but it doesn't work.
I tried to see if with alert() it would work and indeed it does. Why it doesn't do this?
I'm struggling to get out of this problem
<script>
function clickme(){
var elem = document.getElementById('tabs_ul').querySelectorAll('li');
for(var f = 0; f < elem.length; f++){
elem[f].addEventListener('click', changeclass);
}};
function changeclass(){
alert('cliccato'); // This is shown
elem.classList.add('active_tab'); // This doesn't work
};
addEventListener('load', clickme);
</script>
Here the code in the body
<div id="tabs">
<ul id="tabs_ul">
<li class=""><strong>Description</strong></li>
<li class=""><strong>Gallery</strong></li>
</ul>
<div id="tabs-1">
</div>
</div>
elem hasn't been defined in the scope of changeclass
You can use event.target instead:
function changeclass(evt){
evt.target.classList.add('active_tab');
};
Alternatively, you could bind the element to the function:
for(var f = 0; f < elem.length; f++){
elem[f].addEventListener('click', changeclass.bind(elem[f]));
}};
function changeclass(){
this.classList.add('active_tab');
};

Getting the value of the clicked li element

First of all I'm adding an EventListener to the ul, as follows:
action_list_ul.addEventListener("click", set_ua_value, false);
The set_ua_value job is to:
• Listen to every click made on the ul childs (li elements)
• get the value (innerHTML?) of the a tag inside the clicked li
<ul id="action-list">
<li>foo</li>
<li>bar</li>
</ul>
In case foo was clicked on, I need to retrieve the "foo" string.
Since I'm fairly new to javascript, I'm not sure how to get the actual "this"
of the clicked li.
I do not want to use jQuery. Thanks :)
A quick and dirty way is to bind the event to the list, and filter by anchor tags:
JS
var action_list_ul = document.getElementById('action-list');
action_list_ul.addEventListener("click", set_ua_value, false);
function set_ua_value (e) {
if(e.target.nodeName == "A") {
console.log(e.target.innerHTML);
}
}
JS Bin
Alternately, you can filter by LI, and access the anchor through firstChild or childNodes[0].
Use something like:
var win = window, doc = document, bod = doc.getElementsByTagName('body')[0];
function E(e){
return doc.getElementById(e);
}
function actionListValue(element, func){
var cn = element.childNodes;
for(var i=0,l=cn.length; i<l; i++){
if(cn[i].nodeType === 1){
var nc = cn[i].childNodes;
for(var n=0,c=nc.length; n<c; n++){
if(nc[n].nodeType === 1){
nc[n].onclick = function(){
func(this.innerHTML);
}
}
}
}
}
}
actionListValue(E('action-list'), set_ua_value);
Here is an alternative:
var foo = document.getElementById("foo");
foo.addEventListener("click", modifyText);
function modifyText(e) {
console.log(e.target.innerHTML);
}
In this case the binding would have to be with the a elements.
<ul id="action-list">
<li>foo</li>
<li>bar</li>
</ul>
JS BIN

drop down menu will not work

I have a drop down menu that is supposed to work with four different menu choices, each given the same class. But my code is not working. I want it to work with both chrome and IE. The situation where it crashes is in my init method. The console complains as following: Object # has no method 'getElementsByTagName'. Any solution ?
function hideorShowField(list) {
var nodes = list.getElementsByTagName("li");
for (i = 1; i < nodes.length; i++) {
if (nodes[i].style.display == 'none') {
nodes[i].style.display = 'block';
}
else {
nodes[i].style.display = 'none';
}
}
}
function init() {
var list = document.getElementsByClassName("undermeny");
list1.getElementsByTagName("li")[0].onclick = function () {
hideorShowField(list);
};
}
window.onload = init;
My html code:
<ul class="undermeny" >
<li>Opinion</li>
<li>Ledare</li>
<li>Aktuella frågor</li>
<li>Per T Ohlsson</li>
<li>Magda Forsberg</li>
</ul>
<ul class="undermeny" >
<li>Lokalt/Globalt</li>
<li>Malmö</li>
<li>Lund</li>
<li>Limhamn</li>
<li>Burlöv</li>
<li>Eslöv</li>
<li>Höör</li>
<li>Kävlinge</li>
<li>Lomma</li>
<li>Svedala</li>
<li>Staffanstorp</li>
<li>Trelleborg</li>
<li>Vellinge</li>
<li>Sverige</li>
<li>Öresund</li>
<li>Världen</li>
<li>Väder</li>
</ul>
<ul class="undermeny" >
<li>Ekonomi</li>
<li>Nyheter</li>
<li>Privata pengar</li>
<li>Börs</li>
<li>Fonder</li>
</ul>
<ul class="undermeny">
<li>Sport</li>
<li>Fotboll</li>
<li>Ishockey</li>
<li>Handboll</li>
<li>Fridrott</li>
</ul>
getElementsByClassName() returns a nodeList as well as getElementsByTagName(). So you probably need this:
list[0].getElementsByTagName("li")[0].onclick = function () {...};
Or you've to iterate over the listto attach eventhandlers to all 1st lielements within uls with class undermeny. That'll be something like this:
for (var n = 0; n < list.length; n++) {
list[n].getElementsByTagName("li")[0].onclick = function () {...};
}
list1 has not been initialized (although list has). Fix the typo, and then you can iterate over the items in list, calling getElementsByTagName() on each.

Categories