I'm working on attaching a click event on a button which is having class "nextPage", but its not working. Let me show you the code.
function myContent() {
}
myContent.prototype.clickNext = function() {
alert("clicked");
};
var objMyContent = new myContent();
var el = document.getElementsByClassName('nextPage');
el.onclick=objMyContent.clickNext();
Please take a look into it. Please let me know where I did mistake.
You need to reference the function, not execute it, when assigning the click handler.
Instead of:
el.onclick = objMyContent.clickNext();
Use this:
el.onclick = objMyContent.clickNext;
The first piece of code executes clickNext, and assigns it's return value to el.onclick.
The second line assigns a reference to the clickNext function to el.onclick, instead. (Which is what you want)
Also, getElementsByClassName returns an HTMLCollection (Which is basically an array of HTML elements).
You'll need to assign the click handler to each found element in that collection:
for(var i = 0; i < el.length; i++){
el[i].onclick = objMyContent.clickNext;
}
iterate the array and the assign the onclick event
var objMyContent = new myContent();
var el = document.getElementsByClassName('nextPage'); for(var i=0;i<el.length;i++) el[i].onclick=objMyContent.clickNext;
Related
This is a jshint warning question.How can I solve this problem?
var comment_btn=document.querySelector('.comment_button');
var comment_ul=document.querySelector('.comment_ul');
var comment_text=document.querySelector('#comment');
comment_btn.onclick = function(){
var comment_li = document.createElement('li');
comment_li.className = 'comment_li';
if(comment_text.value != '') {
comment_li.innerHTML = comment_text.value + "<a class='comment_a' href='javascript:;'>Delete</a>";
comment_ul.insertBefore(comment_li,comment_ul.children[0]);
var del = document.querySelectorAll('.comment_a');
for (var i = 0; i < del.length; i++) {
del[i].onclick = function() {
comment_ul.removeChild(this.parentNode);
};
}
}
else {
alert('Please input!');
}
};
Warning:
Functions declared within loops referencing an outer scoped variable may lead to confusing semantics. (comment_ul) (W083)jshint(W083)
I really can't think of a solution,please help me.
Every time you add a new li, you are selecting EVERY delete anchor and adding another click event to it.
You should not be looping at all. You should just be selecting the anchor in the li you create and attach the event to that.
const comment_ul = document.querySelector("ul");
const comment_text = document.querySelector("#comment_text");
comment_btn.addEventListener('click', function() {
if (!comment_text.value.length) {
alert('Please input!');
return;
}
var comment_li = document.createElement('li');
comment_li.className = 'comment_li';
comment_li.innerHTML = comment_text.value + "<a class='comment_a' href='javascript:;'>Delete</a>";
comment_ul.insertBefore(comment_li, comment_ul.children[0]);
var del = comment_li.querySelector('.comment_a');
del.addEventListener('click', function(evt) {
evt.preventDefault();
comment_ul.removeChild(this.parentNode);
});
});
<ul></ul>
<input type="text" id="comment_text" />
<button id="comment_btn">Add</button>
The function you assign to onclick inside the loop doesn't care about any value that changes during the course of the loop. It only uses comment_url (defined before the loop) and this.
Create the function before the loop and assign it to a variable.
Copy it to onclick inside the loop.
This is a warning which will appear if you have a loop and you have a variable declared with var, which can sometimes cause problems. Assuming you aren't reassigning comment_ul anywhere, the problems aren't relevant to your situation, so all you need to do is make the linter happy.
One approach would be to declare the variable with const instead (best to declare variables with const when possible, and let when not).
const comment_ul = document.querySelector('.comment_ul');
Another would be to use forEach instead of for(....
That said, your code looks buggy at the moment - you're adding an event listener to every button every time comment_btn is clicked. Instead of doing that, consider adding only a single listener, ever - use event delegation. If the clicked element or one of its ancestors is a .comment_a, remove its parent.
commentContainer.addEventListener('click', (event) => {
const a = event.target.closest('.comment_a');
if (a) {
a.parentElement.remove();
}
});
I am creating elements in list tag dynamically and I want to add different event handler for all elements in list. How can I achieve this?
var menuLinks = document.getElementsByClassName('test');
for(var j = 0; j < menuLinks.length; j++)
{
var menuLink = menuLinks[j];
menuLink.onclick = function(e) {
e.preventDefault();
console.log(menuLink.innerHTML);
};
}
I added the elements in class name test but no matter which ever element I click it always gives me last element.
Your problem is that you are creating a closure inside a loop. See JavaScript closure inside loops – simple practical example for an explanation and generic solution.
In short: JavaScript doesn't have block scope. Since there is only a single menuLink variable, every handler refers to that one variable. The variable can of course only have one value, which is the one that is set in the last iteration.
However, in your case there is a simpler solution: You can use this inside the event handler to refer to the element. You don't need to rely on the loop variable:
menuLinks[j].onclick = function(e) {
e.preventDefault();
console.log(this.innerHTML);
};
Learn more about this in event handlers.
try this,
var menuLinks = document.getElementsByClassName('test');
for(var j = 0; j < menuLinks.length; j++)
{
var menuLink = menuLinks[j];
menuLink.onclick = function(e) {
e.preventDefault();
console.log(e.target.innerHTML);
};
}
I have an array of list items in a piece of Javascript code. I would like to assign an onclick event handler to each one. Each handler would be the same function, but with a different input argument. Right now I have:
function contentfill(i) {
box = document.getElementById("text");
box.style.background="rgba(0,0,0,0.8)";
var content = new Array();
contentdivs = document.querySelectorAll("#contentfill>div");
box.innerHTML = contentdivs[i].innerHTML;
}
li[3].onclick = function() {contentfill(0);};
li[4].onclick = function() {contentfill(1);};
li[5].onclick = function() {contentfill(2);};
This works well enough, but I would like to achieve the same thing with a loop, for example:
for(i=3;i<=5;i++) {
j=i-3;
li[i].onclick = function() {contentfill(j);};
}
This, however, does not work. Since j seems to be defined as 2 at the end of the loop, each time I click, it only seems to call contentfill(2).
For an alternative approach, consider having each of the elements aware of what argument it should be using.
for (var i = 0; i < 3; i++) {
var el = li[i + 3];
el.dataset.contentIndex = i;
el.addEventListener('click', contentfill);
}
Then contentfill would have to extract the argument from .dataset instead of taking an argument, of course. (This is the same mechanism as jQuery's $.data.)
I tend to prefer this since (a) it doesn't generate tons of tiny wrappers, (b) it allows me to later examine and possibly change the "arguments", and (c) it lets me predefine them in the document using data- attributes. Effectively changes them from function arguments into behavior.
The value of i - 3 should be bound to the click handler function; a closure can provide this functionality:
li[i].onclick = (function(j) {
return function() {
contentfill(j);
}
)(i - 3));
Btw, it's better practice to use addEventListener or attachEvent to register click handlers.
I have a problem with the following JavaScript function. I have two UL and I need that when the user clicks on a LI, this element transfers to the other UL.
I've managed to move them onClick from on list to the other, the problem appears when I try to move again a LI that was previously in the other UL, when that happens it just doesn't work...
function testList() {
usersA = document.getElementById("users-a");
usersB = document.getElementById("users-b");
for (var i=0; i < usersA.getElementsByTagName("li").length; i++) {
usersA.getElementsByTagName("li")[i].onclick = function() {
transfer = this.cloneNode(true);
usersB.appendChild(transfer);
usersA.removeChild(this);
return false;
}
}
for (var i=0; i < usersB.getElementsByTagName("li").length; i++) {
usersB.getElementsByTagName("li")[i].onclick = function() {
transfer = this.cloneNode(true);
usersA.appendChild(transfer);
usersB.removeChild(this);
return false;
}
}
}
I know that my logic sucks but it's all I could come up with. Any ideas why it works the first time I transfer a LI but when I try to move back to its original UL it doesn't work?
You're not "moving" elements, you're creating a copy and deleting the original. Although this seems like a "move" from the user's point of view the new elements that you create do not have click handlers assigned. From MDN: "Cloning a node copies all of its attributes and their values but does not copy event listeners."
According to MDN, .appendChild() will remove the child from its current parent so you shouldn't need the two-step clone/remove that you are currently doing. I haven't tested it, but perhaps using just .appendChild() it will keep the handlers? If so you need to remove that handler and assign a new one to allow for which list it now belongs to.
Or, rewrite your handlers so that they check which list is the current parent (have a look at .parentNode) and move to the other list as appropriate.
Bearing in mind that click events "bubble up" starting from the target/source element and up through the parent heirarchy you're probably better off setting your click handlers on the parent <ul> elements and then testing which <li> was clicked. That way you don't have to worry about setting new click handlers on new child <li> elements.
function testList() {
var usersA = document.getElementById("users-a"),
usersB = document.getElementById("users-b");
usersA.onclick = function(e) {
// allow for the IE and non-IE way of handling the event object
if (!e) e = window.event;
var el = e.target || e.srcElement;
if (el.tagName === "li") {
usersB.appendChild(el);
}
}
usersB.onclick = function(e) {
// allow for the IE and non-IE way of handling the event object
if (!e) e = window.event;
var el = e.target || e.srcElement;
if (el.tagName === "li") {
usersA.appendChild(el);
}
}
}
Also, if you're using .getElementsByTagName() call it once in the loop initialisation and assign the result to a variable, then use the variable - don't keep calling the function again and again to test the length, or to access individual elements inside your loop:
for (var i=0, lis = usersA.getElementsByTagName("li"); i < lis.length; i++) {
lis[i].onclick = function() {
// etc
}
}
The problem is that even after you've moved an element from list A to list B, it still keeps its old onclick handler, which still says "remove me from list A and add me to list B". You need to change its onclick handler to say "remove me from list B and add me to list A". Here's one way to fix that:
var usersA = document.getElementById("users-a");
var usersB = document.getElementById("users-b");
var onclickA;
var onclickB = function() {
usersA.appendChild(this);
this.onclick = onclickA;
return false;
};
onclickA = function() {
usersB.appendChild(this);
this.onclick = onclickB;
return false;
};
for (var i=0; i < usersA.getElementsByTagName("li").length; i++)
usersA.getElementsByTagName("li")[i].onclick = onclickA;
for (var i=0; i < usersB.getElementsByTagName("li").length; i++)
usersB.getElementsByTagName("li")[i].onclick = onclickB;
(I also got rid of the cloneNode stuff, for exactly the reasons that nnnnnn gives.)
I have following javascript class:
var ImageGallery = function(arr) {
var ImgArr = arr;
var MainImgId = "";
this.build = function() {
var div = document.createElement("div");
for (var i = 0; i < ImgArr.length; i++) {
var img = document.createElement("img");
img.scr = ImgArr[i];
// what to do next, how to assign loadMainImage function ?????
img.onclick =
div.appendChild(img);
}
}
this.loadMainImage = function(imgPath) {
document.getElementById(MainImgId).src = imgPath;
}
}
next, in code I create an object of that class:
var gallery = new ImageGallery(someArrWithPaths);
gallery.MainImgId = 'idOfMainImg';
gallery.build();
How can I assign img.onclick event to launch loadMainImage(imgPath) function every time I click on the image?
Thanks a lot!
You need to capture the current value of this (which refers to the ImageGallery object) and invoke loadMainImage on it within onclick. Inside onclick, this refers to the image itself, so this.src will give you the image source:
var self = this;
img.onclick = function() {
self.loadMainImage(this.src);
};
I would try to avoid using methods like onevent (in your case onclick), because it is easy to loose this handler (just writing img.onclick somewhere else will override previous handler)
try to use some javascript library (like jQuery), it will simplify your development at least a bit. And back to your question, using YUI 2 I would write so:
var imgCount = ImgArr.length; // do not use ImgArr.length in for definition, it is better
// to save it once and then use... otherwise each time in loop will look up ImgArr object to find length property
for (var i = 0; i < imgCount; i++) {
var img = document.createElement("img");
var src = ImgArr[i];
img.scr = src ;
YAHOO.util.Event.addListener(img, "click", this.loadMainImage, src, this);
}
You can find similar function in jQuery or other frameworks. What it does:
YAHOO.util.Event.addListener(obj, event, handler, argument, context) - attach event listener to obj, for event and handler specified. argument - what to pass to that handler, and context to execute that handler (in your case it will be just this)
Doing this just don't forget to unattach this handler when you unload your page (or whatever you do), e.g. YAHOO.util.Event.removeListener(img, "click", this.loadMainImage), or calling function which removes all listener attached (like YUI's purgeElement). Yes, things are a bit easier when you use img.onclick = ...;, seems like in this case browser will take care about it.