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
Related
In my example here:
Example
JS
$('button').on('click', showHide);
$('.overlay').on('click', showHide);
function showHide(){
$('.scroll-container').toggleClass('show');
$('.content-container').toggleClass('no-scroll');
$('.overlay').toggleClass('opacity');
}
you have a basic body with text. A clickable element (in this case a 'button') causes a scrollable container to appear and 'hover' over the original body, which can be hidden again by clicking outside of this container.
I'm not very good at JavaScript and with this example I was helped by a friend. The thing I'm struggling with now is that I need multiple different clickable elements, displaying a similar scrolling container, but with different content.
I'm doing this for a portfolio website, so imagine a bunch of photos on a page, which when clicked result in a body hovering over the original content, further elaborating the clicked project.
Do I create multiple id's for each project, together with multiple scrolling container id's, and just copy the JavaScript a couple of times?
I hope this makes sense and I hope someone is capable of explaining to me how I'm able to create the proposed effect.
First of all, you have to make a connection between buttons and containers that should be opened. One way is to use their indexes, so that when first button is clicked, first container would open. You can use this reference of the clicked object inside your function, in order to get its index. Like this:
$(this).index()
Then, you have to select all the elements with scroll_container class $('.scroll-container') and reduce the set of matched elements to the one by passing index of the clicked element to .eq() method .eq($(this).index()). Finally, you have to add show class to it addClass('show').
And because the logic is changed, you have to separate actions done on button and .overlay click events. They do not make a reverse action now, so they are not "togglers" anymore.
http://codepen.io/anon/pen/LpWwJL
$('button').on('click', show);
$('.overlay').on('click', hide);
function show(){
$('.scroll-container').eq($(this).index()).addClass('show');
$('.content-container').addClass('no-scroll');
$('.overlay').addClass('opacity');
}
function hide() {
$('.scroll-container').removeClass('show');
$('.content-container').removeClass('no-scroll');
$('.overlay').removeClass('opacity');
}
UPDATE
One thing you should keep in mind regarding $(this).index() method.
As it is written here:
If no argument is passed to the .index() method, the return value is an integer indicating the position of the first element within the jQuery object relative to its sibling elements.
That means that trigger elements should have common parent in order to maintain our logic.
In cases like this: https://stackoverflow.com/posts/32946956/edit, elements that are triggering scroll_container appearance, have different parent nodes (they are placed in 3 different divs). So, if we will call index() method for each of them, it will return '0' because they are the first and the only elements in their parent nodes.
Actually it means that you have to get the order of their parent elements, not theirs own. This can be achieved by using parent() method before index():
$(this).parent().index()
Here is updated codepen.
If I were you, I would implement a generic function to display a different content using the same function based in the button. So for that we will need something to relational the click with the content for that we can set a value in out button:
<button data-id="1">Click me 1!</button>
<button data-id="2">Click me 2!</button>
so out when we click the button we should get the value to send it to our function:
$('button').on('click', function(){
var dataButtonValue = $(this).data('id');
});
Then we can match it with the content using for example data-content-id
<div class="content" data-content-id="1">your wording</div>
<div class="content" data-content-id="2">your wording</div>
With all that we can manage what content we want to show depends on the click.
function showHide(id){
$('.content[data-content-id="' + id + '"]').toggleClass('show');
}
DEMO
I hope it's helps.
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 have a site I am designing that has some nested DIV elements that I use as containers to hold expandable buttons.
The user clicks on a button and it expands to expose some more content.
Everything works great until I have a DIV that is inside of a parent DIV with it's display property set to "none".
Is there any way to force the jQuery .slideUp() method to minimize the expandable buttons regardless of whether it is in a display:none parent or not?
OR, what property/properties do I need to set to make $.slideDown() work properly on an element that was setup up in a display:none parent DIV?
If the parent has display:none; then you'll need to show it before trying to slideUp the child. You can do this with the show function.
$(divToSlide).parent().show();
$(divToSlide).slideUp();
Per the comment, you could alternatively, you could just set the display property to 'block'
$(divToSlide).parent().css("display", "block");
$(divToSlide).slideUp();
Instead of applying slideUp() to the expandable buttons, try setting their "display" value to "none".
This way, when you apply the .slideDown() method it will think that element has already been hidden using the .slideUp() method.
I had the exact same problem and none of the mentioned solutions worked for me the way I expected, as sometimes I lost the slideUp/slideDown animation itself when applying the show() function. My final solution was using a callback function to apply the style directly:
$(divToSlide).slideUp(delay, function () {
// When animation ends
$(this).css('display', 'none');
});
This way I could get the same result without losing the animation.
I want to add a slide up affect with jQuery to a DIV when a link inside of the DIV is clicked, but the problem i am running into is the class of the DIV's are defined by a loop. So, i can't define the DIV class in the jQuery line, because each DIV class is different and i cannot determine ahead of time what they are. I am trying to use .parent and .child but I am not sure how to go about this. Is this making any sense?
Bind to the click of the element you want (in this case I just used a simple anchor element). Then find the first parent that is a div and perform the slideUp() effect.
$('a').click(function() {
$(this).parents('div:first').slideUp();
});
You can see it work here: http://jsfiddle.net/XNnSp/
Let me know if that's what you are looking for http://jsbin.com/ehoza3
$('a').click(function() {
$(this).parent().slideUp();
});
Two (most obvious) ways
FIRST
If your tree is always defined in terms of depth you could access your parent DIV doing just that:
$(this).parent().parent().parent().slideUp();
SECOND
Add an additional class that doesn't clash with dynamic ones and do this:
$(this).closest(".some-custom-class").slideUp();
In a dialog, I would like to show all elements with a specific class. The dialog should hide the rest of the page.
So for example: On this Stack Overflow page, I want to show all elements with class="user-info". Those elements would be shown in a dialog with the same width and height and the same CSS and everything else would be hidden. It would be like cutting them out of the page and pasting them in a dialog.
Any ideas how this could be done?
I would like to show in dialog all
elements with specific class.
So clone those elements, e.g.:
var $div = $("<div />").append($(".fooClass").clone()).dialog();
The dialog should hide the rest of the
page.
Either set the overlay graphic (which you can do using themeroller) to something opaque, or attach some code to the open and close events:
$div.dialog({
open: function(event, ui) { $("body").hide() } // that will hide everything, including the dialog, so watch out.
close: function(event, ui) { $("body").show() }
});
Proof of concept here.
EDIT: This demo keeps the inline style defined in a parent element.
Found an answer thanks to this post
Check it out here. It demonstrates pulling all elements of certain class from iframe and than appending them to main document and copying their style. Problem is that its very slow, especially if we copy many elements with a lot of child elements. If anyone knows a way to improve performance let me know (post here:)).
note: The reason I loaded jsFiddle page in iframe is that it(browser?) won't let jquery inspect content of iframe that's not loaded from same domain.