I am adding event listener to every image in document.
for (var i = 0; i < document.images.length; i++) {
let img = document.images[i];
img.addEventListener('click', function (event) {
var data = {uri: img.src};
});
}
But I can't use es6 so let is making me issue. If I put var instead of let only last image has event.
How to use es5 to add event to each image?
You could do it this way using var and this:
for (var i = 0; i < document.images.length; i++) {
document.images[i].addEventListener('click', function(event) {
var data = {
uri: this.src
};
});
}
for (var i = 0; i < document.images.length; i++) {
document.images[i].addEventListener('click', function(event) {
var data = {
uri: this.src
};
console.log(data)
});
}
<img src="http://www.placehold.it/100x100">
<img src="http://www.placehold.it/100x200">
<img src="http://www.placehold.it/100x300">
Here's an example of creating elements and adding an event listener to each using regular old ES5.
function createClickableElements() {
var arr = [];
for (var i = 0; i < 5; i++) {
var div = document.createElement('div');
div.innerHTML = 'Click Here!';
arr.push(div);
}
return arr;
}
var elems = createClickableElements();
var createClickHandler = function(arg) {
return function() {
console.log(arg);
};
}
for (var i = 0; i < elems.length; i++) {
elems[i].onclick = createClickHandler(i);
document.getElementById('container').appendChild(elems[i]);
}
<div id='container'></div>
Unlike other examples suggest, there is no need of creating a closure for each image, or assigning a new anonymous function to each image. And ES5 already offers Array.from and Array.prototype.forEach. Making use of it supports writing better readable code that just uses two functions - one for assigning the click handler and another one that will handle the data creation of any image, thus offering a less memory consuming approach too ...
function createImageData(evt) {
var data = { uri: evt.target.src };
console.log('createImageData - data : ', data);
}
function assignClickHandler(elmImage) {
elmImage.addEventListener('click', createImageData, false);
console.log('assignClickHandler - elmImage : ', elmImage);
}
Array.from(document.images).forEach(assignClickHandler);
.as-console-wrapper { top: 0; }
<img src="https://placehold.it/100x100">
<img src="https://placehold.it/100x200">
<img src="https://placehold.it/100x300">
... one might even think about making use of event delegation
Related
https://jsfiddle.net/mr_antlers/ryLtwcbe/
I have put this together. I simple image swap on click. I've repeated the same block of code for each face element I want to swap. So this block repeats for eyes, then nose, mouth etc...
//eyes
var img_eyes = []
img_eyes[0] = "http://guildofone.com/makeneki-neko/img/SVG/eyes0.svg";
img_eyes[1] = "http://guildofone.com/makeneki-neko/img/SVG/eyes1.svg";
img_eyes[2] = "http://guildofone.com/makeneki-neko/img/SVG/eyes2.svg";
//Select all elements on the page with the name attribute equal to VCRImage
var eyes = document.querySelectorAll('[name=eyes]');
for(var i=0; i < eyes.length; i++)
{
var eyes = eyes[i];
eyes.addEventListener('click', eyesClicked(), false);
}
function eyesClicked()
{
var counter = 0;
return function(event)
{
counter++;
this.src = img_eyes[counter % img_eyes.length];
}
}
I'd like to cut down the repetition in the arrays and the click listeners...
Ideally I'd also like a button to toggle each face attribute.I haven't got to this yet. A random button would be nice too. Any help on these would be appreciated.
Many thanks in advance for guidance on improving this code.
Just combine all the similar logic into a function and the parts that differ pass in as parameters. In my example, I am passing in the images and the elements to bind an event too - the rest of the implementation within the functions are the same.
window.onload = function() {
//eyes
var img_eyes = []
img_eyes[0] = "http://guildofone.com/makeneki-neko/img/SVG/eyes0.svg";
img_eyes[1] = "http://guildofone.com/makeneki-neko/img/SVG/eyes1.svg";
img_eyes[2] = "http://guildofone.com/makeneki-neko/img/SVG/eyes2.svg";
//face
var img_face = []
img_face[0] = "http://guildofone.com/makeneki-neko/img/SVG/face0.svg";
img_face[1] = "http://guildofone.com/makeneki-neko/img/SVG/face1.svg";
img_face[2] = "http://guildofone.com/makeneki-neko/img/SVG/face2.svg";
//build the rest of the images
//add the features
addFeature( document.querySelectorAll( "[name=eyes]" ), img_eyes )
addFeature( document.querySelectorAll( "[name=face]" ), img_face )
}
function addFeature( features, imgs ) {
//add the feature
for( var i=0; i < features.length; i++ ) {
var feature = features[i];
feature.addEventListener( "click", featureClicked(), false )
}
function featureClicked() {
let counter = 0;
return function( event ) {
counter++;
this.src = imgs[counter % imgs.length];
}
}
}
A couple of opportunities:
Define features as a list
Populate list of feature values dynamically with new Array(3).fill().map()
Add a listener on the containing element of the image instead of the image itself
Maintain the state of the current index on the DOM element using a data attribute (e.g. data-index)
const features = [
{
element: document.querySelector('.eyes'),
values: new Array(3).fill().map((value, index) => `http://guildofone.com/makeneki-neko/img/SVG/eyes${index}.svg`),
},
{
element: document.querySelector('.noses'),
values: new Array(3).fill().map((value, index) => `http://guildofone.com/makeneki-neko/img/SVG/nose${index}.svg`),
}
];
function initialize() {
features.forEach(feature => {
feature.element.addEventListener('click', () => {
const current = parseInt(feature.element.getAttribute('data-index'), 10);
const nextIndex = (current + 1) % feature.values.length;
feature.element.setAttribute('data-index', nextIndex);
updateFeature(feature);
});
updateFeature(feature);
});
}
function updateFeature(feature) {
const index = feature.element.getAttribute('data-index');
const img = feature.element.querySelector('img');
img.src = feature.values[index];
}
initialize();
<div class="eyes" data-index="0">
<img />
</div>
<div class="noses" data-index="0">
<img />
</div>
Trying to figure out onmouseover, onmouseout and onclick with several pictures all having the same ID tag. To do that, I understand I need a .length loop.
This code works without the length loop...
window.onload = setPictures;
function setPictures() {
var img = document.getElementById("pictureBox");
img.onmouseover = mouseOverImage;
img.onmouseout= mouseOutImage;
}
function mouseOverImage() {
var img = document.getElementById("myImg");
img.style.opacity = .5;
}
function mouseOutImage() {
var img = document.getElementById("myImg");
img.style.opacity = 1;
}
This is the loop function I attempted that is not working.
window.onload = setPictures;
function setPictures() {
var img = document.getElementById("pictureBox");
for (var i=0; i<img.length; i++) {
img[i].onmouseover = mouseOverImage;
img[i].onmouseout= mouseOutImage;}
}
Please advise, and thank you in advance for your help!
getElementById only returns one element, as ID's should be unique.
Instead, add a class to each element and select them by class. Callbacks can rely on this's context for your mouse events:
function mouseOverImage() {
this.style.opacity = .5;
}
function mouseOutImage() {
this.style.opacity = 1;
}
window.onload = function setPictures() {
var imageCollection = document.getElementsByClassName('pictureBox');
for (var i=0; i < imageCollection.length; i++) {
imageCollection[i].onmouseover = mouseOverImage;
imageCollection[i].onmouseout = mouseOutImage;
}
}
As it has been said, getElementById return only one element.
This below could help you:
window.onload = setPictures;
function setPictures() {
var img = document.getElementById("pictureBox0");
for (var i=1; img != null; i++) {
img.onmouseover = mouseOverImage;
img.onmouseout= mouseOutImage;
img = document.getElementById("pictureBox"+i);
}
}
As opposed to setting individual functions to each element's click event, I would like to iterate elements and assign based on id or whatever?
document.getElementById('measure').onclick = function() { clickMe(1); };
How would I approach this?
In the past I've done something along these lines:
var setClickOnElements = function(selector) {
$(selector).each(function() {
$(this).click(function(event) {
// behaviour on click
});
});
};
...where selector is a class selector, e.g. .my-class. Within the callback passed to each you can get at other properties of the selected elements, e.g. id etc. If you add that class to the elements you'd like to set a click function and call setClickOnElements('.my-class'); on load, you should be good to go!
EDIT: The above uses jQuery. If you're restricted to pure Javascript, you could use one of the methods described in John Resig's post on implementations of getElementByClass: http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
Here's an example (using Dustin Diaz's method from http://www.dustindiaz.com/getelementsbyclass):
var getElementsByClass = function(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
node = document;
if ( tag == null )
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}
var setClickOnElements = function(className) {
var elements = getElementsByClass(className);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.onclick = function(event) {
// click behaviour here
};
}
};
EDIT: Looking for a non-Jquery Answer
As it stands this code is incredibly long for how simple it is. The only difference between specific instances is a number indicating the ID of the target. Is there a way to accomplish the same result with a single function that gets fed variable based on which div is clicked?
var $ = Sizzle,
p0 = $("#p0")[0];
p1 = $("#p1")[0];
p2 = $("#p2")[0];
p3 = $("#p3")[0];
p4 = $("#p4")[0];
p5 = $("#p5")[0];
p6 = $("#p6")[0];
p7 = $("#p7")[0];
lp0 = $("#productMenu li")[0];
lp1 = $("#productMenu li")[1];
lp2 = $("#productMenu li")[2];
lp3 = $("#productMenu li")[3];
lp4 = $("#productMenu li")[4];
lp5 = $("#productMenu li")[5];
lp6 = $("#productMenu li")[6];
lp7 = $("#productMenu li")[7];
lp0.addEventListener('click',open0,false);
lp1.addEventListener('click',open1,false);
lp2.addEventListener('click',open2,false);
lp3.addEventListener('click',open3,false);
lp4.addEventListener('click',open4,false);
lp5.addEventListener('click',open5,false);
lp6.addEventListener('click',open6,false);
lp7.addEventListener('click',open7,false);
function open0(){
p0.classList.toggle('off');
}
function open1(){
p1.classList.toggle('off');
}
function open2(){
p2.classList.toggle('off');
}
function open3(){
p3.classList.toggle('off');
}
function open4(){
p4.classList.toggle('off');
}
function open5(){
p5.classList.toggle('off');
}
function open6(){
p6.classList.toggle('off');
}
function open7(){
p7.classList.toggle('off');
}
You can try this code:
$('#productMenu li').on('click', function () {
var index = $("#productMenu li").index($(this));
$('#p' + index).toggleClass('off');
});
In plain script, something like the following should work:
// Helper to convert a list to an array
function toArray(list) {
var array = [];
for (var i=0, iLen=list.length; i<iLen; i++) {
array[i] = list[i];
}
return array;
}
// Helper to add a listener without closure
function addFunction(source, target) {
source.addEventListener('click', function(){target.classList.toggle('off')}, false);
}
function addListeners() {
var nodes = [];
for (var i=0; i<8; i++) {
pNodes.push(document.getElementById('p' + i));
}
var lpNodes = toArray(document.getElementById('productMenu').getElementsByTagName('li'));
for (var j=0, jLen=lpNodes.length; j<jLen; j++) {
addFunction(lpNodes[j],pNodes[j]);
}
}
Untested of course, but the approach should help. It isn't really necessary to convert the NodeList returned by getElementsByTagName to an array, but it may be help performance.
There are other approaches to this, most obviously to use event delegation and put a single listener on a parent element (an UL?) that sees where the click came from, then toggles the related element if required. e.g. if the click came from lp0, toggle p0 (though you should probaby be using classes for this, not IDs).
Something like:
function handleClick(event) {
var element;
var target = event.target;
if (target.tagName && target.tagName.toLowerCase() == 'li') {
var id = target.id.match(/\d+/$);
if (id) {
element = document.getElementById('p' + id[0]);
}
if (element) {
element.classList.toggle('off');
}
}
}
window.onload = function() {
document.getElementById('productMenu').addEventListener('click', handleClick, false);
}
Everyone is suggesting some framework, but I'd like to know how it can be done in native JavaScript. I've tried this code and some other things, no effect. Seems that I'm unaware of some basic underlying concept. Any help would be appreciated.
window.onload = function() {
var trCurrent
var main = document.getElementById('main');
var tr = main.getElementsByTagName('tr');
function hl() {
trCurrent.setAttribute('class', 'highlight');
};
for (var x = 0; x < tr.lenght; x++) {
trCurrent = tr[x];
trCurrent.addEventListener ('mousedown', hl, false);
}
};
You have to change trCurrent to this at your function h1, becayse trCurrent points to the last defined TR element (trCurrent = tr[x] for a high x).
Use .length instead of .lenght.
Final code:
window.onload = function() {
var main = document.getElementById('main');
var tr = main.getElementsByTagName('tr');
function hl() {
this.setAttribute('class', 'highlight');
};
for (var x = 0; x < tr.length; x++) {
tr[x].addEventListener ('mousedown', hl, false);
}
};
If you want to use an variable which is subject to change during a loop, it's required to wrap the body in an (anonymous) function:
window.onload = function() {
var main = document.getElementById('main');
var tr = main.getElementsByTagName('tr');
for (var x = 0; x < tr.length; x++) {
(function(trCurrent){ //Anonymous wrapper, first argument: `trCurrent`
function hl() {
trCurrent.setAttribute('class', 'highlight');
};
trCurrent.addEventListener ('mousedown', hl, false);
})(tr[x]); //Passing a reference to `tr[x]`
}
};