I know I can grab the element of an individual ID.
Anyway I can attach a listener to the parent div and pass the ID of the individual span that was clicked?
<div id = "divId">
<span id="one"></span>
<span id="two"> </span>
</div>
JS
document.getElementById("two").addEventListener("click", someFunction);
You can use the event object and access its target property
document.getElementById("divId").addEventListener("click", someFunction);
function someFunction(event) {
console.log(event.target.id);
}
<div id="divId">
<span id="one">one</span>
<span id="two"> two</span>
</div>
If the user clicked on a node that was a child of the element the user wanted to test e.target will be a different node. The sensible way to check this nowadays is to listen at the document level and iterate the path (or use some delegation library, of which there are many):
Add one event listener to the document:
const isOnId = (path,id) => path.some(element => element.id === id);
document.addEventListener('click',function(e) {
if(isOnId(e.path,'two')) {
//you clicked on or in element with an id two
} else {
//you clicked on something else
}
});
Adam's answer is correct and saves a lot of headaches. However there's a better and easiest way to acieve this. Please check this answer
Makes use of Event.currentTarget and it goes like this:
<ul>
<li class="list_item" data-mydata="hello there!">
<img src="..." alt="" width="50", height="50">
</li>
<li class="list_item" data-mydata="hello world">
<img src="..." alt="" width="50", height="50">
</li>
</ul>
<script>
const items = document.querySelectorAll(".list_item");
items.forEach(node => {
node.addEventListener("click", e => {
let myvalue = e.currentTarget.dataset.mydata;
console.log(myvalue); //hello there! || hello world It depends on which element the user has clicked
})
})
</script>
I hope this is useful
Since I do not have enough credits to comment, adding a new answer.
Unlike the answer shared by #CubeInTheBox, I think leveraging the concept of event capturing/bubbling for the respective element makes for a better implementation rather than adding an event listener to each of the target elements.
For the example shared above, the alternative would be:
<ul>
<li class="list_item" data-mydata="hello there!">
<img src="..." alt="" width="50", height="50">
</li>
<li class="list_item" data-mydata="hello world">
<img src="..." alt="" width="50", height="50">
</li>
</ul>
<script>
const parentElement = document.querySelector('ul');
parentElement.addEventListener('click', e => {
// If you want to add the listener on li alone and not on image
if (e.target.className === 'list_item') {
const myvalue = e.target.dataset.mydata;
console.log(myvalue);
}
});
</script>
NOTE that e.currentTarget wouldn’t work for this case, as it would return the parent ul to which the event is bound.
Related
I have an online menu where I'd like to select either noodles or rice, but not both.
When I select a button some CSS happens.
I tried using jquery to do this and it still allows me to select both and doesn't deselect the other.
Am I missing something glaringly obvious?
function selected(clicked) {
var ans=3 - clicked;
var noodles=$("1");
var rice=$("2");
var noodlesID=1;
var riceID=2;
switch (ans) {
case 1: document.getElementById(riceID).classList.toggle("select");
if (noodles.hasClass("select")) {
document.getElementById(noodlesID).classList.toggle("select");
}
break;
case 2: document.getElementById(noodlesID).classList.toggle("select");
if (rice.hasClass("select")) {
document.getElementById(riceID).classList.toggle("select");
}
break;
}
}
/* added by editor for demo purpose */
.selected {
border: 2px dashed red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="productimg" id="1" onclick="selected(this.id)">
<img src="images/plainnoodles.jpg" alt="">
<div class="productName">
Noodles
</div>
</button>
<button class="productimg" id="2" onclick="selected(this.id)">
<img src="images/plainrice.jpg" alt="">
<div class="productName">
Rice
</div>
</button>
There's quite a few issues in your logic which need to be addressed:
Don't mix native JS methods and jQuery. Stick to one or the other, otherwise you end up with some confused code which is more difficult to maintain.
The id selectors in your JS code need to be prefixed with #
The class selectors in your CSS code need to be prefixed with .
The class name you apply in the JS is select yet in the CSS you define it as selected
Don't use inline event handlers in your HTML, ie. onclick. This is bad practice. Use delegated event handlers, bound in JS code. You can then use the this keyword within the event handlers to reference the element which raised the event.
Use DOM traversal methods to relate content to each other to make the logic generic. Don't use things like switch or multiple if conditions to change logic flow for multiple cases - otherwise you will have to update the JS every time the HTML changes, which is exactly what you need to avoid.
You need to remove() the class from the buttons which were not clicked, not toggle() it.
With all that said, here's a working example which caters for an infinite number of buttons:
let productButtons = document.querySelectorAll('button.productimg');
productButtons.forEach(productButton => {
productButton.addEventListener('click', e => {
let button = e.currentTarget;
productButtons.forEach(btn => btn !== button && btn.classList.remove('selected'));
button.classList.toggle('selected');
});
});
.selected { border: 2px dashed red; }
<button class="productimg" id="1">
<img src="images/plainnoodles.jpg" alt="">
<div class="productName">Noodles</div>
</button>
<button class="productimg" id="2">
<img src="images/plainrice.jpg" alt="">
<div class="productName">Rice</div>
</button>
<button class="productimg" id="3">
<img src="images/pizza.jpg" alt="">
<div class="productName">Pizza</div>
</button>
Note that the above is using plain JS. If you wanted to use jQuery instead, the code would look like this:
jQuery($ => {
let $productButtons = $('button.productimg').on('click', e => {
let $button = $(e.currentTarget);
$productButtons.not($button).removeClass('selected');
$button.toggleClass('selected');
});
});
Just use the proper HTML element for this: two radio buttons.
<label><input type="radio" name="dish" value="noodles" /> Noodles</label>
<label><input type="radio" name="dish" value="rice" /> Rice</label>
I have this function
function changeImage() {
console.log(event.target.getAttribute('data-image-path'));
document.getElementById('image-id').src = event.target.getAttribute('data-image-path');
}
on click I want to pass the data-image-path into the changeImage function, the data-image-path is inside the li so is the onclick:
<li data-image-path="./assets/img/my_imgs/features/sc2.png" onclick='changeImage(this.data-image-path)'>
<div class="inside-li">
<img src="./assets/img/my_imgs/features/feat1.png" alt="image">
<p>Easy Scheduling & Attendance Tracking</p>
</div>
<div class="plan-hr">
<hr class="hr-1">
<hr class="hr-2">
</div>
</li>
image:
I have multiple li elements and each contains a different src image inside its data-image-path but how can I pass the data?
This is the img element that should recieve the src:
<div class="image-loader col-md-6 col-sm-12">
<div class="banner-thumb-wrap">
<div class="banner-thumb">
<img class="header-inner-img" id="image-id">
</div>
</div>
</div>
Here is the result in my console.log and code:
Try this, pass this to changeImage function and get the attribute of the clicked element.
function changeImage(obj) {
var imagePath2 = obj.getAttribute('data-image-path');
document.getElementById('image-id').src = imagePath2;
}
<li data-image-path="./assets/img/my_imgs/features/sc2.png" onclick='changeImage(this)'> </li>
You can change your HTML into this:
<li data-image-path="./assets/img/my_imgs/features/sc2.png" onclick='changeImage(event.target.dataset.imagePath)'> </li>
But there is a better version:
Select the li elements with querySelectorAll and addEventListener for them afterward.
Use event.target.getAttribute('data-image-path') to get the clicked li's data-image-path value inside changeImage() method.
function changeImage() {
console.log(event.target.getAttribute('data-image-path'));
document.getElementById('image-id').src = event.target.getAttribute('data-image-path');
}
<img class="header-inner-img" id="image-id">
<li data-image-path="./assets/img/my_imgs/features/sc2.png" onclick='changeImage()'> </li>
I slightly changed your html code, and significantly changed the js code using the forEach method, with a click listener inside. This means that calling the onclick=changeImage (this.data-image-path) event is no longer needed! I also left the console so that you can see which attribute is being loaded on click at the moment.
I hope that is exactly what you needed.
var li_attr = document.querySelectorAll('li');
var img = document.getElementById('image-id');
Array.from(li_attr).forEach(function(li_attrArray, i) {
li_attrArray.addEventListener('click', function() {
imagePath2 = li_attrArray.getAttribute('data-image-path');
img.src = imagePath2;
console.log(img.src);
});
});
<li data-image-path="./assets/img/my_imgs/features/sc1.png">1</li>
<li data-image-path="./assets/img/my_imgs/features/sc2.png">2</li>
<li data-image-path="./assets/img/my_imgs/features/sc3.png">3</li>
<li data-image-path="./assets/img/my_imgs/features/sc4.png">4</li>
<img class="header-inner-img" id="image-id">
You can pass the event and then access the data through the event.target.getAttribute()
function changeImage(e) {
const dataImagePath = e.target.getAttribute('data-image-path');
document.getElementById('image-id').src = dataImagePath;
console.log(dataImagePath);
console.log(document.getElementById('image-id').src);
}
<li data-image-path="./assets/img/my_imgs/features/sc2.png" onclick='changeImage(event)'> </li>
<img class="header-inner-img" id="image-id">
I have a dropdown list with image and text on each link item. The data tags are on the link which wraps both text and image. When I click on the text, the jQuery.fn.init gives me the a.dropdown-item as I'd expect, but if the image gets clicked, it gives me the image object instead with the a.dropdown-item as parent.
I thought I'd specified the filter correctly in the function call, but obviously not - can anyone point me in the right direction?
Dropdown item contruct:
<a class="dropdown-item" href="/i18n/setlang/" data-nextpage="/ca" data-languagecode="ca">
<img src="/static/ca.png" class="d-inline-block align-middle pb-1" alt="" loading="lazy">
Català
</a>
Script start:
$(document).ready(function() {
$('#languagelist a').on('click', function(event) {
event.preventDefault();
let target = $(event.target);
let nextpage = target.data('nextpage');
let url = target.attr('href');
let language_code = target.data('languagecode');
.....
I thought specifying 'a' would just give me the link object, but when the image is clicked that's what comes as target (without data tags obviously).
Any help much appreciated :)
The issue is because the target of the event is always the element at the bottom of the DOM tree which raised the event, not the element which listened for the event.
To achieve what you require you can either use event.currentTarget or the this reference within the jQuery event handler function, as both of these refer to the element which the listener is attached to. Try this:
jQuery($ => {
$('#languagelist a').on('click', function(e) {
e.preventDefault();
// when clicking on the <img />
console.log(this); // = <a />
console.log(event.currentTarget); // = <a />
console.log(event.target) // = <img />
});
});
img {
display: inline-block;
width: 20px;
height: 20px;
background-color: #C00;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="languagelist">
<a class="dropdown-item" href="/i18n/setlang/" data-nextpage="/ca" data-languagecode="ca">
<img src="/static/ca.png" class="d-inline-block align-middle pb-1" alt="" loading="lazy"> Català
</a>
</div>
I want to run a function anytime one of a series of elements are clicked and want to target these elements using a partial ID. Currently, only the first element responds when clicked upon. What have I done wrong? My code:
HTML:
<div>
<a id="selector-0">Zero</a>
</div>
<div>
<a id="selector-1">One</a>
</div>
<div>
<a id="selector-2">Two</a>
</div>
JS:
document.querySelector('[id^="selector-"]').onclick = function(){
var id_selector = this.getAttribute('id');
alert('ID: ' + id_selector);
}
I have attempted changing the querySelector to include 'a[id^="selector-"]', but only the first element ever wants to respond on click.
Use *= selector with id as attribute and document.querySelectorAll.
Then add the onclick to all the elements of the given array of retrieved elements.
const els = Array.from(document.querySelectorAll('[id*=selector-]'));
console.log(els.length);
els.forEach(el => el.onclick = () => console.log('Clicked el:', el));
<div>
<a id="selector-0">Zero</a>
</div>
<div>
<a id="selector-1">One</a>
</div>
<div>
<a id="selector-2">Two</a>
</div>
I am attempting to use JQuery to make 3 thumbnails into buttons that each open up their own page element with details regarding the picture.
Right now I have succeeded in making it so that any thumbnail causes a page element (of the class "description") to scroll open and closed when any thumbnail (from the class "thumbnail") is clicked.
How do I check which thumbnail is clicked on so that I can open a different description corresponding to that specific thumbnail? (This is what I was attempting to do with the "select").
var main = function() {
$('.thumbnail').click(function(select) {
var description = $('.game-descriptions').children('.description');
if( description.is(":hidden")) {
description.slideDown("slow");
}
else
description.hide();
});
}
$(document).ready(main);
Use a data attribute to specify what the thumbnail click is targeting, example: data-target="#game-1", add IDs to your descriptions that match and use data() to use the attribute value of #game-1 a jQuery selector.
Here is a demo
JS
$('.thumbnail').click(function() {
var gameId = $(this).data('target');
$(gameId).slideToggle().siblings(':visible').slideToggle();
});
HTML
<img class="thumbnail" data-target="#game-1" />
<img class="thumbnail" data-target="#game-2" />
<div class="game-descriptions">
<div id="game-1" class="description"></div>
<div id="game-2" class="description"></div>
</div>
Any toggling like toggle(), slideToggle(), fadeToggle() handles the is hidden or is visible
jsFiddle
The parameter to the click function is a jQuery event object, which can be useful in adding some event handling logic. However, within the context of the handler, this refers to the element which triggered the click event, and is typically sufficient for any targeted logic.
Assuming the thumbnails and descriptions have similarly named IDs, for example, you can do something like this:
$(function () {
$('.thumbnail').click(function (event) {
var descId = this.id.replace("thumb", "desc");
var description = $('.game-descriptions').children('#' + descId);
// or simply $("#" + descId);
description.toggle("slow");
});
});
HTML
<div>
<div class="thumbnail" id="thumb-1">Thumb 1</div>
<div class="thumbnail" id="thumb-2">Thumb 2</div>
<div class="thumbnail" id="thumb-3">Thumb 3</div>
</div>
<div class="game-descriptions">
<div class="description" id="desc-1">Description One</div>
<div class="description" id="desc-2">Description Two</div>
<div class="description" id="desc-3">Description Three</div>
</div>
Your technique for targeting the correct 'description' will depend on your actual DOM structure, however.
Also note that I substituted the toggle method for your if statement, as the logic you have is equivalent to what it does (i.e. toggling object visibility).