I’m having trouble with a small mechanics difference between a full-view menu and a mobile-view menu (using the same ul>li>a structure). In the mobile view, the menu hides until the .showMe class is added to its parent div. No problem there, but when I try to select the submenus by using .showMe in the selector chain (in order to perform jQuery actions only when in mobile mode), the script breaks and the dependent actions never occur.
The chunk of selecting code in question is here:
$('.showMe .menu-item-has-children > a').click(function(event){
// prevent default link behavior
if (!($(this).siblings('.sub-menu').hasClass('expanded'))) {
event.preventDefault();
}
// close down any open submenus
$('.sub-menu').removeClass('expanded');
// expand the clicked link’s child ul
$(this).siblings('.sub-menu').toggleClass('expanded');
});
If I remove the .showMe selector from the top line, the effect works great.
See the live site for testing here: http://www.wwva.org.php53-14.ord1-1.websitetestlink.com/students
I feel like I’m going crazy here—what am I missing?
Thanks in advance.
Answered in comments—thanks adeneo!
"The event handler is only added to the elements that match the selector at the time it's bound. Adding a class later means the event handler won't work for those elements, as they didn't match the selector when the event handler was bound. ... You can check for the class inside the event handler."
Related
I'm currently designing a webpage presenting a twitter-like user input that generates an <li> (inside a <ul>) element in which are appended one <h6> element (the post's title) and a <p> element underneath (the content).This works, therefore the input and generation of elements is not the problem.
But what I want to do is use jQuery to hide the posts's content, and toggle it when I click on the post's title. The issue is that the event handler seems to work only for every second post. Whenever i post once more, the 1st post on the list can be toggled, the second not, third yes, etc.
From what I've seen in some answers, I've tried the .click() method, the .on() method, I've tried to replace .toggle() with.hide() and .show() under conditionals, then created a class with display:none to toggle on click. This was my last stop, and the result is described in the above paragraph. Here's the event handler:
$('.postinstance').on("click", "h6.postname", function() {
$(this).siblings().toggleClass('postOff');
});
The .siblings() is actually only the post content, but that's the only way I could get near what I wanted. When I replace $(this).siblings() with the actual class of the content element, every post's content toggles when I click on any title.
How can I make each post open individually when I click on it?
Here's a JSFiddle isolating the input & posts part.
I have looked thoroughly in Stack Overflow and other places, even tutorials, to solve this problem but although similar questions were found none of their answers provided a solution.
You should not attach event handlers to dynamically generated elements directly, instead use some common parent element. Here's a piece of your snippet where I changed the selector and everything started working:
$('.postlist').on("click", "h6.postname", function() {
$(this).siblings().toggleClass('postOff');
});
Important note: you must pull this piece of code out from $('.postbtn').click(..) one level up, otherwise for even number of posts toggling will not work!
And move this out of click handler:
$('.postlist').on("click", "h6.postname", function() {
console.log(this);
$(this).siblings().toggleClass('postOff');
});
Here is a link for a fiddle project I am working on right now. What I am trying to do is to switch active menu element depending on what section is displayed right now on screen. So if it is Kontakti on screen, then Kontakti in menu (<!--NAV BAR-->) has to display as active item. I am not familiar with jS
Add data-role=navigate attribute to ul element where navigation is housed,
In the javascript section of this fiddle,
please try with the following code,
$(function()
{
$("[data-role=navigate]").find("li > a").click(function()
{
$(this).parents("ul").find("li.active").removeClass("active");
$(this).parent().addClass("active");
})
})
I will explain in brief what the code does...
1) Binds a click event handler to <a> inside <li> which is inside <ul> with attribute data-role=navigate
2) When the click happens, it removes the active class for the current element.
3) Assigns the active class to the immediate parent of the <a>
It is a good practice to target specific needs in JS by placing attribute in the DOM elements and hooking up event listeners using that attribute.
Hope it helps!
Bootstrap's Affix might be something that could be useful in this case. It highlights what part of the page is displayed on the screen on a separate sub-navigation part of the page.
Btw, if you have Bootstrap code you can display it on Bootply quite easily. It provides Bootstrap's CSS and JavaScript files by default.
You say you're not familiar with JavaScript but you're asking for functionality that needs JavaScript. I'd recommend trying to use a plugin if it's not something you can write yourself.
Waypoints would do exactly what you're looking for:
http://imakewebthings.com/waypoints/guides/getting-started/
I'm working on a simple website to use at a conference and I'm looking for some help understand the implications of two ways to achieve an effect:
Using .toggle() to show or hide content
This is the method I started with because it is an intuitive action to tap an element to have it's content appear. However, a problem arises when I try to limit one open div at a time.
Summary I'm having trouble limiting the number of opened elements.
Applying an active class with jQuery
Using this method, I can display the hidden content by selecting the child element (see code below), but this stops the user from closing the content by tapping it again. Because I'm expanding divs horizontally, this isn't ideal because of the scroll space that's added.
Summary: How do you close the active div on a second click with this method?
CodePen Demo - Staged site
Relevant Code
This method is using CSS to apply the active class. It works, but like I said above, I'm having a hard time removing the active class from an element tapped again. Use the demo linked above to see how the toggle action works on the page (uncomment lines 8 and 9).
$(".title").click(function() {
//remove active class from other elements
$('.post').removeClass('active');
// Bind to the div
$post = $(this);
// Set active class on .post to control scroll position
$post.parent().toggleClass('active');
// Toggles the hidden .content div
//$post.next().toggle(250);
$('html, body').animate({scrollLeft: $('.active').offset().left},500);
});
The accompanying .active CSS:
.post .content {
display:none;
}
.active {
margin-top:-120px;
}
/* Shows the content div rather than toggling with jQuery */
.active > .content {
display:block;
}
Is there a way I can allow both behaviors (tap to open/close, one open div at a time)? Which method is best suited for that?
You certainly can use toggle() while hiding the other ones. Try something like this:
$(".title").click(function() {
$('.post').not($(this).parent()).hide();
$(this).toggle();
$('html, body').animate({scrollLeft: $(this).parent().offset().left},500);
});
Update: changed .not(this) to .not($(this).parent()) as .title is always child of .post.
Slightly optimised version of #Daniel's solution
$('.title').click(function() {
var clickedPost = $(this).parent('.post')
clickedPost.toggle().siblings('.active').hide();
$('html, body').animate({scrollLeft: clickedPost.offset().left},500);
});
Local var: If you access this, or any other DOM element more than once inside a scope, it's always more efficient to assign it to a local var than wrap it in a JQ object multiple times.
SIblings selector: I don't have a benchmark for this, but running a selector on a subset of the DOM rather than the whole DOM seems intuitively faster. This is more best practice than a large performance hit, but all the little functions add up too.
Chaining JQuery functions: Most JQ functions that act on a JQ element return that element. I can't say that this is more efficient but it's certainly more concise, but this all depends on personal preference.
With very little code you can do this with toggle.
$(".title").click(function() {
$(".post").hide();
$(this).children(".post").toggle();
});
I made it as simple as possible to show the functionality which you could then extend on.
Here is a jsfiddle
EDIT update after comment
I have edited it to now only show 1 at a time and if the 1 currently being shown is clicked it hides it
I also elected to use slideUp() and slideDown() as it seemed to better suit your needs
$(".title").on("click", function(){
if($(this).children(".post").is(":visible")){
$(this).children(".post").slideUp();
}else{
$(".post").not($(this).parent()).slideUp(500);
$(this).children(".post").slideDown(500);
}
});
updated jsfiddle
Can someone explain this to me? I'm trying to understand exactly why drop down lists inside a li tag work okay but when using a form the menu disappears when clicked anywhere.
menu.find('ul li > a').bind('click', function (event) {
event.stopPropagation();
}
It works in combo with:
$("html").click(function() {
menu.find('.active').removeClass('active');
});
Full code with menu example:
http://jsfiddle.net/e4yy4/
This bit of code
$("html").click(function() {
menu.find('.active').removeClass('active');
});
would remove the active class of the menu and so hide it when ever a click is detected anywhere on the page.
But
menu.find('ul li > a').bind('click', function (event) {
event.stopPropagation();});
Would override the first piece of code when the click is detected in the drop down list.
So you can add the below code to override the first piece of code so it also cancel the hiding when clicking in the form.
menu.find('ul > form').bind('click', function (event) {
event.stopPropagation();});
Just like this
http://jsfiddle.net/Quincy/e4yy4/3/
You also want to stop form events propagating to html (should probably be document).
Change the selector to select descendent form elements too.
jsFiddle.
If you change the first check to menu.find('ul li>*') then it seem to work.
I think that line was only handling clicks on links, and your form elements aren't links.
<form> is an element just like any other, as such, it should be eligible for selection using the CSS selectors (which are called by the find() function).
You need to change your selector to include the <form> tag and possibly change the selector to pickup on <input> elements instead of <a>.
For more information on jQuery selectors, please see: http://api.jquery.com/category/selectors/
I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.