Trying to find out how to address individual menu items using "this" - javascript

What I am trying to do is to log results using "this" like an array so that the number 0 logs in the console.log whenever I click the first list item, without pre-defined variables and only using one function.
<ul class="menuList">
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Chicken Cordon Bleu</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Chicken Stir Fry</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Chicken Thighs</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Fish Fillet</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Lasagna</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Pizza</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Salmon</a></li>
<li><a class="menuImage" onclick="openMenuItemAll();"><button>›</button>Spaghetti</a></li>
</ul>
<script type="text/javascript">
function openMenuItemAll(){
var menuImage = document.querySelectorAll(".menuImage");
console.log(this.menuImage);
}
</script>
I'm getting undefined in the console.log when I am trying to get the array number based on the position of the menu item

The value of this depends on how the function is called.
Event handler functions will have this equal to the element to which the event handler is bound.
So onclick="this /* is the element */"
Functions called with no context (e.g. if you call openMenuItemAll();) have this equal to undefined (unless you fail to trigger Strict Mode, in which case it will be window).
Aside: <a> is for links. Don't use it if you aren't linking anywhere. It is forbidden to put a <button> inside a link!
First, fix your markup:
<ul class="menuList">
<li><button>›</button>Chicken Cordon Bleu</li>
Note that the link is removed, and I didn't transfer the class because you can infer it from the class on the list itself.
Then select all the buttons:
const buttons = document.querySelector(".menuList button");
Then loop over the buttons and bind your openMenuItemAll function as the event handler.
Array.from(buttons).forEach(
button => button.addEventListener("click", openMenuItemAll)
);

You need to pass this in your function: onclick="openMenuItemAll(this);
Then you can get the index of the target from the array.
That being said, the onclick event will happen anytime that you click anywhere in the <li> element. If you only want the event to fire when clicking on the button, you need to put the event inside the button. Since the button is a child of the li, then you need to pass this.parentElement to pass the <li> to the function.
Also, since the anchor tag is not really being used for it's purpose, you can remove that entirely.
In the snippet below, I also remove the bullets in the list since the buttons really replace the need for having them.
function openMenuItemAll(target) {
const menuImage = document.querySelectorAll(".menuImage");
const index = Array.prototype.indexOf.call(menuImage, target)
console.log(index);
}
.menuImage{
list-style:none;
}
<ul class="menuList">
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Chicken Cordon Bleu
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Chicken Stir Fry
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Chicken Thighs
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Fish Fillet
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Lasagna
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Pizza
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Salmon
</li>
<li class="menuImage">
<button onclick="openMenuItemAll(this.parentElement);">›</button> Spaghetti
</li>
</ul>

Quentin has your answer, but also consider event delegation to make the code more extensible:
window.onload = function() {
document.querySelector('ul').addEventListener('click', showIndex, false);
}
function showIndex (evt) {
let tgt = evt.target;
let btns = this.querySelectorAll('button');
for (let i=0, iLen=btns.length; i<iLen; i++) {
if (btns[i] == tgt) {
console.log('Button ' + i);
return i;
}
}
return null;
}
<ul>
<li><button>›</button>
<li><button>›</button>
<li><button>›</button>
</ul>
You might also consider using an OL (ordered list) in which case the LI elements will have a value attribute that is their position in the list.
But I think even better is to put an attribute on each button to link to their purpose so that order doesn't matter at all so you have:
<button data-menuItem="123">›</button>Chicken Cordon Bleu
Then you can link the reference "123" to whatever (and you can still get the index if you really want).

Related

Get data-id of anchor element inside a li element

Html
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
My app got a list of things then after 5secs. it will automatically go to the next li.
but that is not the problem. the problem is with the click function I want to know the data-id of the li.
According to the OP's comments
Remove the onclick attribute.
Bind the click event using jQuery.
Use closest('li') to get the parent of your links.
function clickFunction(e) {
e.preventDefault(); // This is to prevent the execution of your links!
console.log($(this).closest('li').data('id'));
}
$('a').click(clickFunction);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li data-id="1">
1
</li>
<li data-id="2">
2
</li>
<li data-id="3">
3
</li>
<li data-id="4">
4
</li>
<li data-id="5">
5
</li>
</ul>
(1) Remove onclick attribute.
(2) Attach a .click handler to all your links
$(function () {
// attach an onclick event handler to your links
$('li a[data-id]').click(function (e) {
// prevent the link from going anywhere
e.preventDefault();
// get the parent li
var li = $(this).closest('li');
// get next li's link data id
console.log(li.next('li').find('a').data('id'));
});
});

Slicknav and delegated click events

I'm rather new to coding and my understanding is limited.
But I'm using Slicknav for mobile screen sizes, however according to the author of the plug in.
"SlickNav menu items are created Dynamically" so I need to use delegated click events or attach event handlers after SlickNav is created.
My SlickNav post
I need some help with this. According to This, I tried making a delegated events.
Here is the original code which is a " direct" event handler (I think)
menuitem.eq(0).on('click', function(){
status = 1;
clearBox();
statusCheck();
});
BTW all my code is trying to do is 1. Clear out a Container which is a display window for content. And 2. Append the correct content to that window based on the menu item that is clicked.
Here is my attempt at a delegated event:
$('#navMenu').on('click',menuitem.eq(0), function(){
status = 1;
clearBox();
statusCheck();
});
Also tried replacing .on() with .delegate(), no dice.
For completeness ill including the functions that the cick events are calling
function clearBox(){
$("#display_box").children().fadeOut(1000).appendTo(".holding");
};
function statusCheck(){
if (status == 1){
displaycontent.eq(0).fadeIn(1000).appendTo("#display_box");
displaycontent.eq(0).removeClass("hide");
$("#display_box").animate({scrollTop:0},500);
} else{}
if (status == 2){
displaycontent.eq(25).fadeIn(1000).appendTo("#display_box");
displaycontent.eq(25).removeClass("hide");
$("#display_box").animate({scrollTop:0},500);
} else{}
// Etc Etc Etc
Edit: Provided HTML for the Menu
<div class ="menu_wrap">
<nav id = "navMenu">
<ul class ="clearfix menu">
<li>General
<ul class="subMenu1">
<li class ="menu_item">Introduction</li>
<li class ="menu_item"> What you need</li>
<li class ="menu_item">House Rules</li>
<li class ="menu_item">Running the Game</li>
<li class ="menu_item">Survival</li>
<li class ="menu_item">Encounters</li>
</ul>
</li>
<li>The World
<ul class ="subMenu1">
<li class ="menu_item">Nol</li>
<li class ="menu_item">Wol</li>
<li class ="menu_item">Sol</li>
<li class ="menu_item">Eol</li>
</ul>
</li>
<li>Locations and Maps</li>
<li>Races and Cultures
<ul class ="subMenu1">
<li> <a class="allow_default" href="index_npcs.html" target="blank">NPC Creatures</a></li>
<li class ="menu_item"> Voran Kingdom</li>
<li class ="menu_item">Doval Empire</li>
<li class ="menu_item">Salatai Sultanate</li>
<li class ="menu_item">Gamoran Republic</li>
<li class ="menu_item">Elandel</li>
<li class ="menu_item">Kingdom of Night</li>
<li class ="menu_item">Halflings</li>
<li class ="menu_item">Aiur' Dun</li>
<li class ="menu_item">Half-Elves</li>
<li class ="menu_item">Half-Orcs</li>
<li class ="menu_item">Dryads</li>
</ul>
</li>
<li> Organizations
<ul class ="subMenu1">
<li class="menu_item">Information</li>
<li class ="menu_item">The Green Wardens</li>
<li class ="menu_item">The Temple of Light</li>
<li class ="menu_item">The Black Hand</li>
<li class ="menu_item">The Stone Priests</li>
<li class ="menu_item">The Golden Company</li>
<li class ="menu_item">The Dread Guards</li>
</ul>
</li>
<li class ="menu_item">Character Creation
<ul class ="subMenu1">
<li class ="menu_item"> <a class="allow_default" href="index_personality_test.html" target="blank">Creation Test</a></li>
</ul>
</li>
</ul>
</nav>
</div>
Try something like this:
<nav id="navMenu">
<ul class="clearfix menu">
<li>General
<ul class="subMenu1">
<li class="menu_item" data-item="intro">Introduction</li>
<li class="menu_item" data-item="requirements">What you need</li>
<li class="menu_item" data-item="rules">House Rules</li>
...
And this for the JavaScript:
$('#navMenu, .slicknav_menu').on('click', '.menu_item', function() {
clearBox();
switch($(this).attr('data-item')) {
case 'intro': {
// Do something here.
break;
}
case 'requirements': {
// Do something here.
break;
}
case 'rules': {
// Do something here.
break;
}
// More cases...
}
});
It appears the cloned menu created by SlickNav is placed directly under the <body> element, so you cannot call .on() only on your #navMenu element. You could call it on the body element, but the code above calls it on both the #navMenu and .slicknav_menu elements. The <ul> element generated by SlickNav has the slicknav_menu class on it.
As written, the code above has to be called after you call .slicknav() since it requires the cloned menu exist when it is called. Otherwise, you would have to change to $('body').on(....
As for the rest, the selector '.menu_item' identifies all the menu item elements, so the click-event handler will execute for all of them. But each menu item element has a different value for the data-item attribute. That way you can do something different for each one.
jsfiddle

get clicked element in javascript?

I want add class to clicked li and delete other li class
<ul>
<li onclick="s()"><span>خانه</span></li>
<li onclick="s()"><span>سفارش</span></li>
<li onclick="s()" class="selected"><span>آپلود</span></li>
<li onclick="s()"><span>درباره ما</span></li>
<li onclick="s()"><span>تنظیمات</span></li>
</ul>
I can do it with jquery but now i want do this with javascript?
Don't use inline click handlers. Instead, attach a handler from JavaScript.
<ul id="myUL"> <!-- This is just for example, to make it easier to select -->
<li><span>خانه</span></li>
<li><span>سفارش</span></li>
<li class="selected"><span>آپلود</span></li>
<li><span>درباره ما</span></li>
<li><span>تنظیمات</span></li>
</ul>
Then, in JavaScript:
var myUL = document.querySelector('#myUL');
// Attach one event listener on the parent, instead of one for each element.
// It's more performant and will work with dynamically added entries!
myUL.addEventListener('click', function(event) {
// Here, event.target is the actual event clicked.
// Remove class from selected one.
document.querySelector('#myUL .selected').classList.remove('selected');
// And add it to the current one
event.target.classList.add('selected');
});
Working Example
This is a quick fix and this way is not recommended. But in your case, you need to use this way:
function s (which) {
document.querySelectorAll(".clicked")[0].classList.remove("selected");
which.classList.add("selected");
}
And change the call this way:
<ul>
<li onclick="s(this)"><span>خانه</span></li>
<li onclick="s(this)"><span>سفارش</span></li>
<li onclick="s(this)" class="selected"><span>آپلود</span></li>
<li onclick="s(this)"><span>درباره ما</span></li>
<li onclick="s(this)"><span>تنظیمات</span></li>
</ul>
The right way is to use eventListeners and bind the events to an ID.
var list = document.querySelector('#menu');
list.addEventListener('click', function(event) {
list.querySelector('.selected').classList.remove('selected');
event.target.classList.add('selected');
});
And add the ID to the <ul>:
<ul id="menu">
<li>خانه</li>
<li>سفارش</li>
<li class="selected">آپلود</li>
<li>درباره ما</li>
<li>تنظیمات</li>
</ul>

Console function not defined error?

I do not understand why my function isn't working and why I keep getting a "test is not defined error" when I try to run it.
I want the page to scroll down to the appropriate section, when the user clicks the right menu item.
<ul class="timelineNav">
<li class="navItem">
<button type="button" onclick='test("1")'>
Event1
</button>
</li>
<li class="navItem"><a class="navItem" href="url">Event2</a></li>
<li class="navItem"><a class="navItem" href="url">Event3</a></li>
<li class="navItem"><a class="navItem" href="url">Event4</a></li>
<li class="navItem"><a class="navItem" href="url">Event5</a></li>
</ul>
<article id="post1">
some text
</article>
<article id="post2">
some text
</article>
<article id="post3">
some text
</article>
Javascript function:
function test(postLocation){
var post = $('#post'+postLocation).offset();
$(window).scrollTop(post.top);
};
I originally wanted to make the list item itself start the function, but I guess it has to be a button?
You don't show where the function test() is defined, but if it is inside a document-ready handler (or any other function), it is not in the global scope, so it cannot be called from an onclick attribute.
Either:
(1) Stop using an onclick attribute and bind a click event handler to the elements.
(2) Move the definition of the function outside the document-ready handler.
(3) Defined the function like this:
window.test = function(postLocation) {
var post = $('#post'+postLocation).offset();
$(window).scrollTop(post.top);
};
Also, you do not have to use a button. You should be able to make it so the function executes by a click on a list item.
I suggest the following:
HTML:
<ul class="timelineNav">
<li>Event 1</li>
<li>Event 2</li>
<li>Event 3</li>
<li>Event 4</li>
</ul>
<article id="post1">
some text
</article>
<article id="post2">
some other text
</article>
JQuery:
$('.timelineNav').children('li').children('a').click(function(event) {
event.preventDefault();
var $article = $($(this).attr('href'));
$(window).scrollTop($article.offset().top);
});
JSFiddle DEMO
Although the <a> elements are not needed, they do cause the mouse pointer to change.
Updated HTML
Added the id to the button for better jquery selection
<ul class="timelineNav">
<li class="navItem">
<button type="button" id="clickbutton">Event1</button>
</li>
...
JS
Attach event to your button, remove inline event click
$(function () {
$('.navItem').on('click', '#clickbutton', function () {
var location = 1; //define or fetch your location here
var post = $('#post' + location).offset();
alert(post);
$(window).scrollTop(post.top);
});
});
Demo

switching two ids with ajax callback

I was kindly helped by Jonathan over here simple javascript question-linking button state to image swap?
The problem is that this makes the "active" class the same class for both list items.
Each list item needs to toggle its own active and its own inactive class (each is a button with its own css styling and background image).
Can you please help me modify the script so that I can do that?
Here is Jonathans provided code:
<li class="transcript">
<a id="transcriptionhorbutton" class="inactive"
href="javascript:void()"
onclick="getDataReturnText('/lessons/transcriptions/ajaxcalls/L1horizontal.txt', callback);make_active(this);"></a>
</li>
<li class="transcript">
<a id="transcriptionvertbutton" class="inactive"
href="javascript:void()"
onclick="getDataReturnText('/lessons/transcriptions/ajaxcalls/L1vertical.txt', callback);make_active(this);"></a>
</li>
<script>
var buttons = [ document.getElementById("transcriptionvertbutton"),
document.getElementById("transcriptionhorbutton")];
function make_active(el) {
deactivate_buttons();
el.setAttribute("class","active");
}
function deactivate_buttons() {
buttons[0].setAttribute("class","inactive");
buttons[1].setAttribute("class","inactive");
}
</script>
I understand that the problem is here:
function make_active(el) {
deactivate_buttons();
el.setAttribute("class","active");
}
but I don't know enough to separate that into two different classes.
Just add an extra parameter to the function:
function make_active(el, classname) {
deactivate_buttons();
el.setAttribute("class",classname);
}
Then change your calls just a bit. Here is the completed code. Note I changed all calls of setAttribute to .className instead. This was just so you don't run into any trouble with IE6:
<li class="transcript">
<a id="transcriptionhorbutton" class="inactive"
href="javascript:void()"
onclick="getDataReturnText('/lessons/transcriptions/ajaxcalls/L1horizontal.txt', callback);make_active(this,'active_class_1');"></a>
</li>
<li class="transcript">
<a id="transcriptionvertbutton" class="inactive"
href="javascript:void()"
onclick="getDataReturnText('/lessons/transcriptions/ajaxcalls/L1vertical.txt', callback);make_active(this,'active_class_2');"></a>
</li>
<script>
var buttons = [ document.getElementById("transcriptionvertbutton"),
document.getElementById("transcriptionhorbutton")];
function make_active(el, classname) {
deactivate_buttons();
el.className = classname;
}
function deactivate_buttons() {
buttons[0].className = "inactive_class_1";
buttons[1].className = "inactive_class_2";
}
</script>

Categories

Resources