addEventListener gone after appending innerHTML - javascript

Okay, so i have the following html added to a site using javascript/greasemonkey.
(just sample)
<ul>
<li><a id='abc'>HEllo</a></li>
<li><a id='xyz'>Hello</a></li>
</ul>
and i've also added a click event listener for the elements.
All works fine up to this point, the click event gets fired when i click the element.
But... i have another function in the script, which upon a certain condition, modifies that html,
ie it appends it, so it looks like:
<ul>
<li><a id='abc'>Hello</a></li>
<li><a id='xyz'>Hello</a></li>
<li><a id='123'>Hello</a></li>
</ul>
but when this is done, it breaks the listeners i added for the first two elements...
nothing happens when i click them.
if i comment out the call to the function which does the appending, it all starts working again!
help please...

Any time you set the innerHTML property you are overwriting any previous HTML that was set there. This includes concatenation assignment, because
element.innerHTML += '<b>Hello</b>';
is the same as writing
element.innerHTML = element.innerHTML + '<b>Hello</b>';
This means all handlers not attached via HTML attributes will be "detached", since the elements they were attached to no longer exist, and a new set of elements has taken their place. To keep all your previous event handlers, you have to append elements without overwriting any previous HTML. The best way to do this is to use DOM creation functions such as createElement and appendChild:
var menu = pmgroot.getElementsByTagName("ul")[0];
var aEl = document.createElement("a");
aEl.innerHTML = "Hello";
aEl.id "123";
menu.appendChild(aEl);

As an variant, you can add such html, which will work without addEventListener:
<ul>
<li><a id='abc' onclick='myFunc()'>Hello</a></li>
<li><a id='xyz' onclick='myFunc()'>Hello</a></li>
// The following add dynamically
<li><a id='123' onclick='myFunc()'>Hello</a></li>
</ul>

U can also use jQuery's 'live' function

Related

DOM Manipulations

I'm still struggling with this question.
Add an Event Handler to the li element and console.log() the name of the shirt they selected. Use JavaScript
<h3>Shirts</h3>
<ul id='list'>
<li>Biker Jacket</li>
<li>Mens Shirt</li>
</ul>
If i get your question right, use jQuery:
$('li').click(function() {
console.log($(this).text());
});
It looks like this is a homework question. And that you are new to StackOverflow.
On StackOverflow you should always write pieces of code that you have attempted.
Anyways, because you are new... I can show you 2 ways of accomplishing this.
Please note that I am not so good in vanilla Javascript aswell. So
there is probably a better way to accomplish what you're trying.
1) Inline Event Handler
You can add inline event handlers directly to the HTML, like this:
<h3>Shirts</h3>
<ul id='list'>
<li onclick='console.log(this.innerText)'>Biker Jacket</li>
<li onclick='console.log(this.innerText)'>Mens Shirt</li>
</ul>
2) Non-inline Event Handler
The difference here is that you need to refer to the exact elements you want to add an Event Listener to. The benefit of doing this, is that you don't have to add an Event Listener manually to each list element. So the HTML stays clean.
var list = document.getElementById("list"); // select the parent element
var items = list.getElementsByTagName("li"); // select all <li> items in this parent element
for (var i = 0; i < items.length; i++) { // loop through all items
items[i].addEventListener("click", function() { // add the click event listener to the item
console.log(this.innerText); // console.log() the item's text.
});
}
Also read the following MDN documentations to learn about the used functions:
getElementById
getElementByTagName
Loops and iterations
innerText
Happy learning!

Accessing a reference to the onclick event from a li a click

This is the first time I’ve thought about moving my events outside of the normal HTML onClick=”” event but I cant seem to find any references as to how I would do this with a li list.
Basically I’m trying to get the number associated with the scrollToArtical(#) in to myElement.onclick. How would you rewrite this so that the event is in the .js file.
I’ve tried variations of to get at the element but these don’t work:
var objScrollToNav = document.getElementById("id_ScrollToNav").children;
var objScrollToNav = document.querySelector("#id_ScrollToNav a");
Any help would be greatly appreciated – CES
My old code is:
<ul id="id_ScrollToNav" role="list">
<li class="sectionNavOff"><a onclick="scrollToArticle(0)" role="link">•</a></li>
<li class="sectionNavOn"><a onclick="scrollToArticle(1)" role="link">•</a></li>
<li class="sectionNavOff"><a onclick="scrollToArticle(2)" role="link">•</a></li>
</ul>
Use document.querySelectorAll to get an array-like list, then loop over them. To keep a reference to the index, make sure you also pass the index into a new closure (the addEvent function below creates a new closure).
function scrollToArticle(index) { console.log("Scrolling to:", index); }
// Select all the elements.
var links = document.querySelectorAll("#id_ScrollToNav a");
// This function adds event listener, and holds a reference to the index.
function addEvent(el, index) {
el.addEventListener("click", function() {
scrollToArticle(index);
});
}
// Loop over the elements.
for (var i = 0; i < links.length; i++) {
addEvent(links[i], i);
}
<ul id="id_ScrollToNav" role="list">
<li class="sectionNavOff"><a role="link">•</a></li>
<li class="sectionNavOn"><a role="link">•</a></li>
<li class="sectionNavOff"><a role="link">•</a></li>
</ul>
Since your li elements can be gathered up into an array and arrays have indexes, you really don't need to pass a hard-coded number to your function. You can just pass the index of the li that is being clicked to the function.
Also, don't use <a> elements when they are not directly navigating you anywhere. This can cause problems for people who use screen readers. Instead, set up the click event directly on the li elements and eliminate the a elements completely.
Lastly, don't use inline HTML event attributes (onclick). That is how we did event handlers 20 years ago and, unfortunately, this technique just won't die. There are many reasons not to use them. Instead, follow modern standards and separate your JavaScript from your HTML.
// Get all the li elements into an array
var items = Array.prototype.slice.call(document.querySelectorAll("#id_ScrollToNav > li"));
// Loop over the list items
items.forEach(function(item, index){
// Assign each item a click event handler that uses the index of the current item
item.addEventListener("click", function(){ scrollToArticle(index) });
});
// Just for testing
function scrollToArticle(articleNumber){
console.log(articleNumber);
}
#id_ScrollToNav > li {
cursor:pointer;
}
<ul id="id_ScrollToNav" role="list">
<li class="sectionNavOff" role="link">•</li>
<li class="sectionNavOn" role="link">•</li>
<li class="sectionNavOff" role="link">•</li>
</ul>
To add to the above, use data- attributes to separate css styles from javascript (meaning, html class tags should be used for html/css things only).
<li data-element="sectionNavOff">
<li data-element="sectionNavOn">
There are some minor downsides to using data- tags, mainly speed, but many enterprise applications and frameworks (e.g. Bootstrap) tend to believe the upside to separating styles from javascript completely outweighs the downsides. If I knew whether or not you use jQuery I could give you a detailed usage example.

Appending div from another html file

I have 2 menu buttons which supposed to append divs from an external html.
Now I have this:
<ul>
<li><a href="#" onclick="javascript:test2();" value="#CS" >CS</a></li>
<li><a href="#" onclick="javascript:test2();" value="#PS" >PS</a></li>
</ul>
And 2 divs on the external html, that just have simple text in them saying "text1" and "text2".
I use this code to append the data:
$.get('external/list.html', function (data) {
$(data).filter('#PS').appendTo('#content')
});
How can I "upgrade" it to take for example an attribute like value, and use it to choose what div to append? Because I prefer to have it as 1 function instead of making 2.
Also this code appends a div, but it doesn't reset it. It just keeps spamming the same div over and over and over. How can I stop it?
Option 1
Keep a single call to your $.get, as you are doing now, but pass reference of your clicked element through your function as below:
<ul>
<li><a href="#" onclick="javascript:test2(this);" value="#CS" >CS</a></li>
<li><a href="#" onclick="javascript:test2(this);" value="#PS" >PS</a></li>
</ul>
JS
function test2(ctrl)
{
var element=$(ctrl).attr('value');
$.get('external/list.html', function (data) {
$(data).filter(element).appendTo('#content');
//element will have value either #PS or #CS
});
}
Note - .appendTo will keep on appending the data to your DOM, instead use .html which replaces contents everytime
Ex:
$(data).filter(element).html('#content');
Option 2
You can also attach click event to your a tags with a common class given for both thus removing inline function call as below:
<ul>
<li>CS</li>
<li>PS</li>
</ul>
JS
$(".menu").on('click',function(){
var element=$(this).attr('value'); //this refers to clicked element
$.get('external/list.html', function (data) {
$(data).filter(element).appendTo('#content');
//element will have value either #PS or #CS
});
});
Update
Ideally html should work but I imposed it in wrong way. You just need to change one thing as below:
$("#content").html($(data).filter(element));

Applying an onclick event to an element that doesn't exist on page load

I have styled a list to look like a select box and I want to fire a function when the user clicks an element in the list however the element is loaded via AJAX and hence isn't there when the page loads and I can't bind an onclick event to it onDomReady.
If I had it as a normal select list I could just tag on an onChange event to the <select> field, but I obviously can't do that in my case.
My HTML:
<div id="main_category_field" class="fields">
<div class="cat_list">
<ul>
<li class=""><a rel="1866" href="#1866">Sports Cards ></a></li>
<li class=""><a rel="1867" href="#1867">Gaming Cards ></a></li>
<li class=""><a rel="1868" href="#1868">Non-Sport Cards ></a></li>
<li class=""><a rel="1869" href="#1869">Supplies & Storage ></a></li>
<li class=""><a rel="1940" href="#1940">Video Games </a></li>
</ul>
</div>
<div class="contentClear"></div>
</div>
How can I run a function whenever the user clicks any of these options? Also would be great if you could also advise how to pass the respective value of the rel back when they click an option.
Using jQuery so options in that would be preferred.
Edit: Meant to say that the main element main_category_field is a static element. The elements inside are loaded via AJAX.
you need to delegate your elements to static parent , if the element is added dynamically using on()
try this
$(document).on('click','li a',function(e){
e.preventDefault();
var rel = this.rel;
//or using attr()
var rel=$(this).attr('rel');
alert(rel);
});
however delegating it to its closest static parent(present at a time of insertion) is better than document itself.. so if you are adding the list inside main_category_field div.. then you can use
$('#main_category_field').on('click','li a',function(e){
//same stuff
The key word is event delegation. If you want to assign event handlers to dynamically added elements for which you know their "future" selectors, you should use the .on() method on an (already existing) parent element of those dynamic elements.
The second parameter to .on() is then the selector of the dynamically added elements
$(document).on('click', '.cat_list li a', function(e) {
alert(this.rel); // show the "rel" attribute of the clicked a element
e.preventDefault(); // to prevent the default action of anchor elements
});
To bind an event handler to an element that does not yet exist on the page use on.
$(document).on("click", "#main_category_field", function(e){
e.preventDefault();
var rel = e.target.rel;
});
JS Fiddle: http://jsfiddle.net/82bAb/
Use .on for listen dynamically created dom elements as follows
$(document).on('click','div.cat_list ul li a',function(){
var rel=$(this).attr('rel');//to get value of rel attribute
alert(rel);
//other operations
});

Jquery select all children within children

I'm sure this will be a simple question but I still struggle with DOM selectors in Jquery so here is a model of my html code:
<fieldset class="product-options" id="product-options-wrapper">
<ul class="options-list">
<li><a href>Item1.1</a></li>
<li><a href>Item1.2</a></li>
<li><a href>Item1.3</a></li>
</ul>
...other html items here
<ul class="options-list">
<li><a href>Item2.1</a></li>
<li><a href>Item2.2</a></li>
<li><a href>Item2.3</a></li>
</ul>
</fieldset>
Now how do I select all of the 'li a' items in both lists (or X number of lists) with class name .options-list and bind them with a click function.
Currently I have:
$('fieldset#product-options-wrapper ul.options-list > li a').bind('click', function(e) {
//code here
});
And it only gets the first options-list.
Thanks, greatly appreciated!
EDIT: If i click on a Item2.X list item first, then it will grab that options list. But as soon as I click on the Item1.x list items it disregards the second .options-list
If you are going to bind to each li element, you should bind it to the ul element instead (helps greatly with performance when there are a lot of events).
$('.options-list', '#product-options-wrapper').bind('click', function(e)
{
e.preventDefault();//In case you don't want to go to a different page
var clicked = e.target;//The href that was clicked
/* If you only want this to happen if the a tag was clicked, add the following line
if(clicked.tagName == 'A')*/
//Rest here
});
How about $('.options-list a').bind('click', function(e) { });?
You can use delegate in this case to make it even simpler. Try this
$('#product-options-wrapper ul.options-list').delegate('li > a', 'click', function(e) {
//code here
});
Your method seems sound to me. I created a test fiddle using your HTML (and an extra anchor to prove that it won't get the click added) and your JS (with minor modifications).
http://jsfiddle.net/chrisvenus/esZxH/1/
The selector you had did work but since you said you wanted the a to be a direct child of the li (or at least I read it that way) I slightly tweaked it in my version above. ARe you sure its not just your function is not doing quite what you want while executing or can you confirm that your click function isn't being run at all?

Categories