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.
Related
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>
Apologies in advance I feel as if this is a very simple solution and I am in the middle of a severe brain fart. I am simply trying to grab the data-id of an item I am clicking using ES6 syntax but no matter which way I run it I keep getting undefined.
HTML
<ul>
<li class="list-item" data-id="item-1">Click me</li>
<li class="list-item" data-id="item-2">Click me</li>
<li class="list-item" data-id="item-3">Click me</li>
</ul>
JS
let $el = $('ul');
class List {
constructor($el) {
this.$el = $el;
this.$listItem = $el.find('li');
this.attachHandlers();
}
attachHandlers() {
this.$listItem.on('click', () => {
var data = $(this).attr('data-id');
console.log(data);
});
}
}
const _init = () => {
if ($el.length) {
$el.each((i, v) => {
new List($(v));
});
}
};
_init();
Ive simplified the code down as much as possible, anybody see what I am doing wrong here?
The problem is in your attachHandlers method. Since you are using an arrow function, this refers to the class and not the element that was clicked.
Change the arrow function to a regular function.
In addition to Josan's answer, if you want to continue using the arrow function, use it like this:
class List {
constructor($el) {
this.$el = $el;
this.attachHandlers();
}
attachHandlers() {
this.$el.on('click', 'li', (e) => {
var data = $(e.currentTarget).attr('data-id');
console.log(data);
});
}
}
For more discussion, refer to this thread.
Simpler and easier by just using JQuery:
$(".list-item").on('click', function() {
console.log($(this).data('id'));
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="list-item" data-id="item-1">Click me</li>
<li class="list-item" data-id="item-2">Click me</li>
<li class="list-item" data-id="item-3">Click me</li>
</ul>
This question already has answers here:
Javascript onclick
(4 answers)
Closed 7 years ago.
I'm new in javascript and I'm trying to hide or show a submenu with the onclick event.
Here's the code:
javascript:
<script type="text/javascript">
bool shown = false;
document.getElementById("test").onclick = SubMenuDisplay();
function SubMenuDisplay()
{
alert("Hello World!");
if(!show)
{
$('subm').show();
shown = true;
}
else
{
$('subm').hide();
shown = false;
}
}
</script>
and html:
<li id="test">Account
<ul id="subm">
<li>Login</li>
<li>Sign up</li>
</ul>
</li>
but my onclick event doesn't work at all.
Is someone can help?
Here is what I believe you are trying to pull off:
Account
<ul id="subm">
<li>Login
</li>
<li>Sign up
</li>
</ul>
var shown = false;
$("#subm").hide();
function SubMenuDisplay() {
alert("Hello World!");
if (!shown) {
$('#subm').show();
shown = true;
} else {
$('#subm').hide();
shown = false;
}
}
Fiddle: http://jsfiddle.net/ryanpcmcquen/wyrogc77/
JavaScript is dynamically typed, so you do not need to declare types, as you did with bool shown = false;. This can be replaced with var shown = false;. JavaScript will figure out that this is a boolean.
Beyond that your code just needed a little massaging. You were assigning the id="test" to the li element, when you should just assign it to the a, and style the a like an li, with something like:
#test {
list-style-type: bullet;
list-style-position: inside;
display: list-item;
}
Just remove the ():
document.getElementById("test").onclick = SubMenuDisplay;
You are assigning the event with the function not the return value of the function. And make sure you are doing it after declaring the function, else it might throw an undefined error.
function SubMenuDisplay()
{
alert("Hello World!");
if(!show)
{
$('subm').show();
shown = true;
}
else
{
$('subm').hide();
shown = false;
}
}
document.getElementById("test").onclick = SubMenuDisplay;
Since you decided to use jQuery, then use it (I mean, go to jquery.com and read the doc):
$('#test > a').click(function (e) {
e.preventDefault();
$(this).next().toggle();
});
.subm {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li id="test">
Account
<ul class="subm">
<li>Login</li>
<li>Sign up</li>
</ul>
</li>
I have a function that targets the li elements of a div id called mk-featured-kits. All of this works perfectly.
(function() {
var myNode = document.getElementById('mk-featured-kits');
myNode.addEventListener("mouseover", function(e) {
console.log(e);
if (e.target.tagName === 'LI') {
// all happens here
}
})();
I am using Chrome to see the console log and it allows me to use this path for the LI's: e.target.tagName.
Since IE 8 and bellow doesn't read addEventListener event, I am using this (code bellow) to check for the feature. The problem is by using this technique I can't access the LI using: e.target.tagName === 'LI' because the event only sees myNode as a tagName === DIV.
(function() {
var myNode = document.getElementById('mk-featured-kits');
if (myNode.addEventListener) { // all browsers except IE before version 9
myNode.addEventListener ("mouseover", function () {myEvent (myNode)}, false);
} else {
if (myNode.attachEvent) { // IE before version 9
myNode.attachEvent ("onmouseover", function () {myEvent (myNode)});
}
}
function myEvent(myNode) {
console.log(myNode);
if (myNode.target.tagName === 'LI') {
// all happens here
}
})();
How can I access the LI as I did on the first script but using the technique of the second script. Thank you in advance.
This is the html code:
<div class="mk-column">
`<div id="mk-featured-kits">
<ul>
<li id="kit01">Link 01</li>
<li id="kit02">Link 02</li>
<li id="kit03">Link 03</li>
<li id="kit04">Link 04</li>
<li id="kit05">Link 05</li>
<li id="kit06">Link 06</li>
<li id="kit07">Link 07</li>
<li id="kit08">Link 08 </li>
</ul>
</div>`
`
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/