Getting element text value after the class has been added - javascript

I am trying to get the text value of an element that gets a class added after being clicked on. But I cannot get it. I also tried using html().
let selectLevel;
function selectOption(menu) {
$(menu).on('click', "li", function() {
$(menu).find('.activeSelection').removeClass();
$(this).addClass('activeSelection');
});
}
$(document).ready(function() {
selectOption('.levelList');
selectLevel = $('.levelList').find('.activeSelection').text();
});

The easiest way I can imagine is to trigger a custom event when you click on the list element, then bind a function to this event to update your variable.
Example below:
let selectLevel;
function selectOption(menu) {
$(menu).on('click', "li", function() {
$(menu).find('.activeSelection').removeClass();
$(this).addClass('activeSelection');
$(menu).trigger('itemChanged'); //trigger custom event
});
}
$(document).ready(function() {
selectOption('.levelList');
$('.levelList').on('itemChanged', function() { // wait for event on your menu
selectLevel = $(this).find('.activeSelection').text();
console.log(selectLevel); // just logging the current value
});
});
.activeSelection {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="levelList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>

Related

How do i toggle a class on and off whenever i click on a list item?

I am trying to toggle a class on and off whenever i click on a list item. I have tried several things like using classList.toggle and wrapping the list elements in a anchor tag and then trying to add a class to the list items through that but the more things i try the more confused i get.
Here below is the snippet of my code.
var li = document.querySelectorAll("li");
li.addEventListener('click', (e) => {
addDoneClass();//
});
function addDoneClass() {
li.className = "done"
}
.done {
text-decoration: line-through;
}
<ul>
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
You might look at my JavaScript code an wonder why i done it that way that is because i was very confused at this point and that was the last thing that i tried.
const elements = document.querySelectorAll("li");
elements.forEach((element) => {
// First option
element.addEventListener('click', function() {
this.classList.toggle('active');
});
// Second option
//element.addEventListener('click', function() { customHandle(this); });
// Third option
//element.addEventListener('click', (e) => customHandle(e.target.closest('li')));
});
// For the second and third options
//function customHandle(element) {
// element.classList.toggle('active');
//}
li.active {
text-decoration: line-through;
}
<ul>
<li>Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
In my opinion, it's better if you add one event listener to the ul element, than have multiple for the li elements.
You can use classList.add("class") and classList.remove("class") functions, more information. You will also need to check if the current item already has the class, for that use the classList.contains("class") function.
Here's an example (updated with ternary operator):
const ul = document.getElementById("list");
ul.addEventListener('click', (e) => {
const li = e.target;
li.classList.contains('done') ? removeDoneClass(li) : addDoneClass(li)
});
function addDoneClass(li) {
li.classList.add("done");
}
function removeDoneClass(li) {
li.classList.remove("done");
}
.done {
text-decoration: line-through;
}
<ul id="list">
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>
Tip: use the classList.toggle("class"), it does the same thing, but it's the cleaner way.
You Have To Use "this" Keyword To Reffer The Currently Clicked Element. You Can Achieve This Using JQuery Like This.
li.active { text-decoration : line-through; }
$("li").click(function () {
this.classList.toggle('active');
});
The problem is with scope.
function addDoneClass() {
li.className = "done"
}
In your context, li is an array of list items. Without running, I would guess that clicking on any list item was striking through all of the list items. Instead you need to pass the specific list item that you want changed.
li.addEventListener('click', (e) => {
addDoneClass(this); //this is the list item being clicked.
});
function addDoneClass(obj) {
// Now you can change to toggle if you want.
obj.className = "done"
// ie obj.className.toggle("done");
}
querySelectorAll returns a nodelist and addEventLister can only be applied to one node at a time. So to fix your code you should loop over each li in the list. And since you are using the event (evt) with addEventListener, you can use event.currentTarget to get the element clicked.
var listItems = document.querySelectorAll("li");
var listItemCount = listItems.length;
for (var i = 0; i < listItemCount; i++) {
listItems[i].addEventListener('click', (evt) => {
addDoneClass(evt);//
});
}
function addDoneClass(evt) {
evt.currentTarget.className = "done";
}
.done {
text-decoration: line-through;
}
<ul>
<li random="23">Notebook</li>
<li>Jello</li>
<li>Spinach</li>
<li>Rice</li>
<li>Birthday Cake</li>
<li>Candles</li>
</ul>

$(document).on() in plain JavaScript?

In jQuery there is .on() which can be used as:
$(document).on('click', '.foo', function() { /* ... */ });
This listens for click events on all DOM elements with the class .foo.
However, this also listens for any eventual elements added to the DOM later, so it is not equal to:
var elements = document.getElementsByClassName('foo');
for (var element in elements) {
element.addEventListener('click', function() { /* ... */ });
}
How do I do this in plain JavaScript? Am I supposed to use a MutationObserver? If so, then how? If not, then what?
That called event delegation, in the pure javascript you could attach the click event to the parent element then on click check if the clicked element match the target you want to click and perform the action you want ,like the example below :
document.getElementById("parent-item").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was an item with class 'foo'
if(e.target && e.target.className == "foo") {
console.log("foo "+e.target.innerText+" was clicked!");
}
});
Hope this helps.
document.getElementById("parent-item").innerHTML += "<li class='foo'>Item 3</li>";
document.getElementById("parent-item").addEventListener("click", function(e) {
if(e.target && e.target.className == "foo") {
console.log("foo "+e.target.innerText+" was clicked!");
}
});
<ul id="parent-item">
<li class='foo'>Item 1</li>
<li class='foo'>Item 2</li>
</ul>

How to select next matched child-item in an ul and trigger a click event

Can anyone help me with this problem:
I want to trigger a click event on the next href nested inside an unordered list on an keyup-event but I can't get it running.
The HTML looks like this:
<ul>
<li>Start</li>
<li>Topic 1</li>
<li>Topic 2</li>
<li>Topic 3</li>
</ul>
and the jQuery looks like this:
$(document).keyup(function (e) {
if (e.keyCode == 40) { // down
$('#active').next("li a").click();
}
});
You are trying to select the succeeding li sibling element of the a#active element. It doesn't have an li sibling elements, but its parent element does.
You need to select the parent, and then select the sibling li element from there.
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').parent().next().find('a').click();
}
});
You could also use the .closest() method, if the nesting varies:
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').closest('li').next().find('a').click();
}
});
You may actually have to access the first matched DOM element in order to fire the click event as well (since .click() is a native DOM element method), so you could also try:
$(document).keyup(function(e) {
if (e.keyCode === 40) { // down
$('#active').closest('li').next().find('a')[0].click();
}
});

Why is event undefined when using a.click

I am trying to create a basic megamenu for my site but I'm having problems with the menu opening and closing on one click. This is a basic version of the HTML:
<ul class="menu">
<li class="dropdown-toggle">
Toggle Dropdown
<ul class="dropdown-wrap">
<ul class="column">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<ul class="column">
<li>Link 4</li>
<li>Link 5</li>
<li>Link 6</li>
</ul>
</ul>
</li>
</ul>
And this is a basic version of the JavaScript:
$menu = $('ul.menu');
$menu.find('li.dropdown-toggle').children('a').click(function(event) {
var $wrap = $(this).siblings('.dropdown-wrap');
if ($wrap.hasClass('open')) {
closeDropdown($wrap);
} else {
openDropdown($wrap);
}
event.preventDefault().stopPropagation();
});
$('html').click(function() {
console.log('HTML FIRED');
if ($('.dropdown-wrap.open').length > 0) {
closeDropdown($('.dropdown-wrap.open'));
}
});
function closeDropdown($wrap) {
$wrap.removeClass('open');
console.log('Close');
}
function openDropdown($wrap) {
$wrap.addClass('open');
console.log('Open');
}
The problem is I'm getting an event is undefined error when running stopPropagation.
Uncaught TypeError: Cannot read property 'stopPropagation' of undefined
This is a jsfiddle of the error in action: http://jsfiddle.net/styphon/0go9ukky/
My questions are why is event undefined in this case? And if event is undefined why doesn't event.preventDefault() throw the same error? How can I get this working?
None of the search results have explained this behaviour so far and I can't understand why event is undefined.
event is defined. But preventDefault does not return the event. So you have to do it in two lines:
event.preventDefault();
event.stopPropagation();
You cannot chain methods from event object, ex: stopPropagation with preventDefault.
Code From jQuery Source:
preventDefault: function () {
var e = this.originalEvent;
this.isDefaultPrevented = returnTrue;
if (e && e.preventDefault) {
e.preventDefault();
}
},
As you can see that preventDefault does not return anything explicitly, so by default undefined is returned.
And you'll get error
Uncaught TypeError: Cannot read property 'stopPropagation' of undefined
To solve the issue use
event.preventDefault();
event.stopPropagation();
You can also use return false; as alternative to the two methods.

Get index of element as child relative to parent

Let's say I have this markup:
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
And I have this jQuery:
$("#wizard li").click(function () {
// alert index of li relative to ul parent
});
How can I get the index of the child li relative to it's parent, when clicking that li?
For example, when you click "Step 1", an alert with "0" should pop up.
$("#wizard li").click(function () {
console.log( $(this).index() );
});
However rather than attaching one click handler for each list item it is better (performance wise) to use delegate which would look like this:
$("#wizard").delegate('li', 'click', function () {
console.log( $(this).index() );
});
In jQuery 1.7+, you should use on. The below example binds the event to the #wizard element, working like a delegate event:
$("#wizard").on("click", "li", function() {
console.log( $(this).index() );
});
something like:
$("ul#wizard li").click(function () {
var index = $("ul#wizard li").index(this);
alert("index is: " + index)
});
There's no need to require a big library like jQuery to accomplish this, if you don't want to. To achieve this with built-in DOM manipulation, get a collection of the li siblings in an array, and on click, check the indexOf the clicked element in that array.
const lis = [...document.querySelectorAll('#wizard > li')];
lis.forEach((li) => {
li.addEventListener('click', () => {
const index = lis.indexOf(li);
console.log(index);
});
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Or, with event delegation:
const lis = [...document.querySelectorAll('#wizard li')];
document.querySelector('#wizard').addEventListener('click', ({ target }) => {
// Make sure the clicked element is a <li> which is a child of wizard:
if (!target.matches('#wizard > li')) return;
const index = lis.indexOf(target);
console.log(index);
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Or, if the child elements may change dynamically (like with a todo list), then you'll have to construct the array of lis on every click, rather than beforehand:
const wizard = document.querySelector('#wizard');
wizard.addEventListener('click', ({ target }) => {
// Make sure the clicked element is a <li>
if (!target.matches('li')) return;
const lis = [...wizard.children];
const index = lis.indexOf(target);
console.log(index);
});
<ul id="wizard">
<li>Step 1</li>
<li>Step 2</li>
</ul>
Take a look at this example.
$("#wizard li").click(function () {
alert($(this).index()); // alert index of li relative to ul parent
});
Delegate and Live are easy to use but if you won't have any more li:s added dynamically you could use event delagation with normal bind/click as well. There should be some performance gain using this method since the DOM won't have to be monitored for new matching elements. Haven't got any actual numbers but it makes sense :)
$("#wizard").click(function (e) {
var source = $(e.target);
if(source.is("li")){
// alert index of li relative to ul parent
alert(source.index());
}
});
You could test it at jsFiddle: http://jsfiddle.net/jimmysv/4Sfdh/1/
Yet another way
$("#wizard li").click(function ()
{
$($(this),'#wizard"').index();
});
Demo
https://jsfiddle.net/m9xge3f5/

Categories

Resources