I'm trying to create a very simple tab switching mechanism using jQuery (it's for a WordPress plugin where all tabs show fields that must belong to the same form.)
My goal is:
1) the clicked tab gets highlighted as active;
2) the content referred to the clicked tab gets visible taking the place of the previously displayed content;
3) only the currently active content must be visible and only its tab must be highlighted.
My HTML code is something like this:
<html>
<body>
<h2 class="nav-tab-wrapper">
First
Second
Third
</h2>
<hr />
<section id="tab-1" class="tab-content active">
<P>Some content here.</P>
</section>
<section id="tab-2" class="tab-content hidden">
<p>Some more here.</p>
</section>
<section id="tab-3" class="tab-content hidden">
<p>Something else here.</p>
</section>
</body>
</html>
and my broken jQuery code is:
(function( $ ) {
$('h2.nav-tab-wrapper > a').click(function(event) {
// Clicked anchor: prevent browser from taking action.
event.preventDefault();
// jQuery selector to display tab content.
var active_tab_selector = $('h2.nav-tab-wrapper > a').attr('href');
// Find the activated navigation and remove '.active'.
var activated_nav = $('h2.nav-tab-wrapper > a.nav-tab-active');
activated_nav.removeClass('nav-tab-active');
// Add '.active' to the clicked navigation.
$(this).parents('a').addClass('nav-tab-active');
// Hide the displayed tab content.
$(active_tab_selector).removeClass('active');
$(active_tab_selector).addClass('hidden');
// Show target tab content.
var target_tab_selector = $(this).attr('href');
$(target_tab_selector).removeClass('hidden');
$(target_tab_selector).addClass('active');
});
})( jQuery );
It doesn't work as expected. You can try it on JSFiddle (with some minimum CSS omitted here.)
I'm new to jQuery. What am I missing or doing wrong?
Thank you in advance.
Using sibling() selector
$('.nav-tab').click(function(e) {
//Toggle tab link
$(this).addClass('nav-tab-active').siblings().removeClass('nav-tab-active');
//Toggle target tab
$($(this).attr('href')).addClass('active').siblings().removeClass('active');
});
.nav-tab-active {
color: #f00;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2 class="nav-tab-wrapper">
First
Second
Third
</h2>
<hr />
<section id="tab-1" class="tab-content active">
<P>Some content here.</P>
</section>
<section id="tab-2" class="tab-content">
<p>Some more here.</p>
</section>
<section id="tab-3" class="tab-content">
<p>Something else here.</p>
</section>
Several things I would recommend:
The wrapper should include the tab content panes as well. This way if you have multiple tab containers on the same page, the wrapper knows which tabs and panes belong together.
Write jQuery features using a plugin. Defining $.fn.tabs allows us to do things like $(selector).tabs() instead of putting a big mess of code in our (function ($) { ... })(jQuery) on-ready block
the hidden class is redundant and making your program harder to write. If a tab (or tab content pane) has the active class, it is active - otherwise it is not active.
Because the plugin toggles active on all child components in a wrapper, this may cause screen flickering (depending on your CSS) if we click on a tab that is already active. We should prevent the plugin from firing on active tabs.
Use event delegation with .on to attach an event listener to each wrapper instead of using .click to attach an event listener to each tab. Using more event listeners results in programs that are slower to respond to events. Use as few listeners as possible.
It'd be nice if we could customize which tab and content pane is initially active. Perhaps the user clicks /about#contact-us and that goes to the About page with the Contact Us tab active. Unless the HTML has the active class set for the Contact Us tab, it will not be displayed. This is a more advanced feature, so we cover it in a separate section at the end of this answer.
Here's a full working demo. Adjustments have been made to include recommendations above -
// write new features as a plugin
$.fn.tabs = function () {
// for each tab wrapper ...
return this.each(function () {
// capture wrapper context
var wrapper = this
// use single click event per wrapper, delegate only to inactive tabs
$(wrapper).on('click', '.nav-tab:not(.active)', function (event) {
// remove all active
$('.active', wrapper).removeClass('active')
// get the clicked tab
var clicked = $(event.target)
// add the active class
clicked.addClass('active')
// find the tab's content and add the active class
$(clicked.attr('href'), wrapper).addClass('active')
})
})
};
// keep on-ready function nice and clean!
(function ($) {
$('.tab-wrapper').tabs()
})(jQuery)
.nav-tab.active {
font-weight: bold;
}
.tab-content {
display: none
}
.tab-content.active {
display: inherit;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="tab-wrapper">
<nav>
First
Second
Third
</nav>
<hr />
<section id="tab-1" class="tab-content active">
<P>Tab Contents 1</P>
</section>
<section id="tab-2" class="tab-content">
<p>Tab Contents 2</p>
</section>
<section id="tab-3" class="tab-content">
<p>Tab Contents 3</p>
</section>
</div>
<div class="tab-wrapper">
<nav>
First
Second
Third
</nav>
<hr />
<section id="tab-1" class="tab-content active">
<P>Some content here.</P>
</section>
<section id="tab-2" class="tab-content">
<p>Some more here.</p>
</section>
<section id="tab-3" class="tab-content">
<p>Something else here.</p>
</section>
</div>
How to pass options to plugins -
As a beginner, it's unlikely you have have written your own jQuery plugin before. As you can see above, there's almost nothing to it. The one piece missing is the ability to send options or arguments to the plugin -
// make our function accept an argument, userOptions
$.fn.tabs = function (userOptions) {
// merge default options with userOptions
var options =
$.extend(true, $.fn.tabs.defaults, userOptions || {})
return this.each(function () {
// capture wrapper context
var wrapper = this
// setup event listener (truncated)
$(wrapper).on('click', '.nav-tab:not(.active)', ...)
// click the initial tab
$(options.initialTab, wrapper).click()
});
};
// default options
$.fn.tabs.defaults =
{ initialTab: '.nav-tab:eq(0)' };
Now if you forget to put active class on your tabs or panes, the plugin will still activate the initial tab based on the default selector, the first tab, .nav-tab:eq(0).
To create a tab container with a custom initial tab, we pass an option to our plugin. For example the third tab, .nav-tab:eq(2) -
$('.tab-wrapper').tabs({ initialTab: '.nav-tab:eq(2)' })
Remove the ACTIVE from all to set the active on the designed tab.
$('.nav-tab').click(function(e){
var target_tab_selector = $(this).attr('href');
$('.tab-content').addClass('hidden');
$('.tab-content').removeClass('active');
$(target_tab_selector).removeClass('hidden');
$(target_tab_selector).addClass('active');
})
.active{display:block}
.hidden{display:none}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<h2 class="nav-tab-wrapper">
First
Second
Third
</h2>
<hr />
<section id="tab-1" class="tab-content active">
<P>Some content here.</P>
</section>
<section id="tab-2" class="tab-content hidden">
<p>Some more here.</p>
</section>
<section id="tab-3" class="tab-content hidden">
<p>Something else here.</p>
</section>
</body>
</html>
I'm kind of in a hurry now, but the first thing that is missing is to hide all the "active" ones when you click on some tab, add:
$('.tab-content').removeClass('active');
after your preventDefault() and give it a try.
Related
I'm making a small page that has 3 sections. While 1 section is showing the others are hidden. When the page loads the welcome section is displayed, while the other sections are set to display:none. Clicking the menu button for the other pages shows the desired section and hides all the others; I am using jQuery to do that. Unfortunately, now I come across the problem that I'm unable to make a url to go to the specific section. Usually, to go to a section of a page that is not hidden, I would just create an anchor and name is XXX, and then add #XXX at the end of that page's url, but doing this on a hidden div doesn't make the div show.
Any suggestions?
html:
<div id="menu">
<p>Home</p>
<p>Page 1</p>
<p>Page 2</p>
</div>
<div id="home">
<h1>Welcome!</h1>
<p>This is where all the home page stuff will go</p>
</div>
<div id="page1">
<h1>Page 1</h1>
<p>Page 1 content here</p>
</div>
<div id="page2">
<h1>Page 2</h1>
<p>Page 2 content here</p>
</div>
css:
#page1 {
display:none;
}
#page2 {
display:none;
}
js:
$(document).ready(function(){
$('#menu-home).click(function(){
$('#home').show('fast');
$('#page1').hide('fast');
$('#page2').hide('fast');
});
$('#menu-page1).click(function(){
$('#page1').show('fast');
$('#home').hide('fast');
$('#page2').hide('fast');
});
$('#menu-page2).click(function(){
$('#page2').show('fast');
$('#home').hide('fast');
$('#page1').hide('fast');
});
});
You could use window.location.hash* which will return the corresponding part of the url.
For this to work you should give your <a> tags some proper hash value:
<p>Page 1</p>
<p>Page 2</p>
And check the mentioned string whether it matches a given page:
$(document).ready(function(){
//binding click events to elements
var locationHash = window.location.hash.substring(1);
if(locationHash == 'page1'){
$('#menu-page1').trigger('click');
}else if(locationHash == 'page2'){
$('#menu-page2').trigger('click');
}
});
You see I used the click-events for a fast quick-n-dirty solution and also substringed location.hash to get rid of the #.
Of course this is open for improvement .e.g. not hiding page1 or page2 at all on page load if a given hash is found.
*See the linked document for Location as window.location is a Location object which holds a hashproperty
You will need some javascript, that, on page loading, checks the URL for an anchor like #menu-xy, and makes the corresponding div visible.
I'm making a small page that has 3 sections. While 1 section is showing the others are hidden. When the page loads the welcome section is displayed, while the other sections are set to display:none. Clicking the menu button for the other pages shows the desired section and hides all the others; I am using jQuery to do that. Unfortunately, now I come across the problem that I'm unable to make a url to go to the specific section. Usually, to go to a section of a page that is not hidden, I would just create an anchor and name is XXX, and then add #XXX at the end of that page's url, but doing this on a hidden div doesn't make the div show.
Any suggestions?
html:
<div id="menu">
<p>Home</p>
<p>Page 1</p>
<p>Page 2</p>
</div>
<div id="home">
<h1>Welcome!</h1>
<p>This is where all the home page stuff will go</p>
</div>
<div id="page1">
<h1>Page 1</h1>
<p>Page 1 content here</p>
</div>
<div id="page2">
<h1>Page 2</h1>
<p>Page 2 content here</p>
</div>
css:
#page1 {
display:none;
}
#page2 {
display:none;
}
js:
$(document).ready(function(){
$('#menu-home).click(function(){
$('#home').show('fast');
$('#page1').hide('fast');
$('#page2').hide('fast');
});
$('#menu-page1).click(function(){
$('#page1').show('fast');
$('#home').hide('fast');
$('#page2').hide('fast');
});
$('#menu-page2).click(function(){
$('#page2').show('fast');
$('#home').hide('fast');
$('#page1').hide('fast');
});
});
You could use window.location.hash* which will return the corresponding part of the url.
For this to work you should give your <a> tags some proper hash value:
<p>Page 1</p>
<p>Page 2</p>
And check the mentioned string whether it matches a given page:
$(document).ready(function(){
//binding click events to elements
var locationHash = window.location.hash.substring(1);
if(locationHash == 'page1'){
$('#menu-page1').trigger('click');
}else if(locationHash == 'page2'){
$('#menu-page2').trigger('click');
}
});
You see I used the click-events for a fast quick-n-dirty solution and also substringed location.hash to get rid of the #.
Of course this is open for improvement .e.g. not hiding page1 or page2 at all on page load if a given hash is found.
*See the linked document for Location as window.location is a Location object which holds a hashproperty
You will need some javascript, that, on page loading, checks the URL for an anchor like #menu-xy, and makes the corresponding div visible.
I have a large number of pages containing varying lengths of long text (1000 words+) that I have separated into logical tabs (Summary, Content, References and Authors) using CSS only. The Content tab has far more text than the other tabs and users still end up scrolling like crazy to read through it all.
I would like a way of splitting up the long Content text into sub-tabs or sub-pages, but without allowing the user to navigate away from the main tabbed page (i.e. I still want them to be able to switch between the 4 main tabs (Summary, Content, References and Authors).
I also, do not want to have to go through all the content and manually enter in the breakpoints for the Content section as the content may be changing fairly frequently.
Any ideas about how to split the Content section up to allow the 4 tab navigation and display the Content without having to scroll?
I have created a codepen with a sample of what it currently looks like with html and CSS code: http://codepen.io/TimSparrow/pen/xdKAa
<div class="chapters">
<article class="tabs">
<section id="tab1">
<h2>Summary</h2>
<p><h3>Summary</h3></p>
<ul>
<li>sample</li>
</ul>
</section>
<section id="tab2">
<h2>Content</h2>
<p>Long Text Goes Here</p>
</section>
<section id="tab3">
<h2>References</h2>
<p>This content appears on tab 3.</p>
</section>
<section id="tab4">
<h2>Authors</h2>
<p>This content appears on tab 4.</p>
</section>
</article>
</div>
Welcome to SO,
You could try using the overflow css property which handles how content should behave if it would overflow the borders of the containing element, and add a height to your content (so it will have the aforementioned content border limit), something as follows:
Wrap your content into a containing element, for example <div class="sectionContent">:
....
<section id="tab1">
<h2>Summary</h2>
<div class="sectionContent">
<h3>Summary</h3>
<p>Long text here</p>
....
....
</div> // div.sectionContent closed
</section>
....
And then add some CSS to the bowl to make it work:
.sectionContent{
overflow-y : scroll;
height : 15em;
color:#000;
}
Comment: I've also added the color attribute to this class' CSS, because you're referencing section > p (as in this case you don't have any direct p children within the sections, they are actually section > .sectionContent > p.)
I would like to show the contents of a div when I navigate to it via a URL. Currently the Div contents are revealed onclick.
How would I get the Div contents to be revealed when using a URL with anchor to navigate to the main title in the ?
Here's javascript
<script type="text/javascript" src="jquery-latest.js"></script>
<script type="text/javascript">
$(document).ready(function(){
//Set default open/close settings
$('.acc_container').hide(); //Hide/close all containers
//$('.acc_trigger:first').addClass('active').next().show(); //Add "active" class to first trigger, then show/open the immediate next container
//On Click
$('.acc_trigger').click(function(){
if( $(this).next().is(':hidden') ) { //If immediate next container is closed...
$('.acc_trigger').removeClass('active').next().slideUp(); //Remove all .acc_trigger classes and slide up the immediate next container
$(this).toggleClass('active').next().slideDown(); //Add .acc_trigger class to clicked trigger and slide down the immediate next container
}
return false; //Prevent the browser jump to the link anchor
});
});
</script>
here's my html
<h2 class="acc_trigger">Some Title Goes Here</h2>
<div class="acc_container">
<div class="block">
<h3>Strap Line Goes here</h3>
Text content goes here
</div>
</div>
<h2 class="acc_trigger">Another title Goes Here</h2>
<div class="acc_container">
<div class="block">
<h3>Another Strap Line Goes here</h3>
Some more text content goes here
</div>
</div>
If you're using a hash (e.g. url.com/page.html#otherStuff) then read the location.hash
http://www.w3schools.com/jsref/prop_loc_hash.asp
If you're using a query (e.g. url.com/page.html?otherStuff) use the location.search
http://www.w3schools.com/jsref/prop_loc_search.asp
Put the code below the DIV that you want to show, so the element has been created. Then just do a simple if(hash == "#otherStuff"){ /* Do stuff */ } Where in "Do stuff" you would just run the open function.
listen to the hashchange event which is triggered on the window object. then you can show the div or do what you want with it.
Something along the lines of this;
About
<div id="mydiv_about" style="display:none">
</div>
Media
<div id="mydiv_media" style="display:none">
</div>
and some javascript
window.addEventListener("hashchange", function(){
document.getElementById("mydiv_"+document.location.hash.substr(1)).style.display="block";
},false);
//edit
updated to reflect a working example
I have two buttons that are essentially "next" and "previous" buttons for my tabbed form with bootstrap. The code works when you go from the introduction tab to the applicant tab just fine the way it is. The only problem is there are 11 "tabs" in the element. I am needing essential parts in this <a> to change on each tab.
What I am needing to change is the href , .style.width , and the two classNames at the end of the `onclick' function each time the user progresses through the form.
Previous
Next
The sections that need to correspond with the buttons are identified with an id that is different for all 11 sections.
<div class="tab-pane" id="confirmation">
So for example when the users clicks the button "next" from the introduction tab I need the above to change to: (and vise versa for the previous button)
Previous
Next
and so on throughout the tab-panes.
I am figuring I could do something like, I just really don't know how to start it and how to change just the specific onclick elements like I am needing to.
if(this tab-pane is something?)
{
document.getelementById('previous').href="";
document.getelementById('previous').style.width="";
document.getelementById('previous').className="";
}
As Requested in the comments the tabs are laid out like so:
<div class="tab-content">
<div class="tab-pane" id="introduction">
//content here
</div>
<div class="tab-pane" id="applicant"> //and so on just like this for all 11 tabs.
//content here
</div>
</div>
Here you go...
This is using Twitter bootstrap and Jquery...
Hopefully its helpful to you...
The progress bar is a bit of a hack, but its a start and Im sure you can figure things out from there...
The only thing that sucks is that the tabs are clickable, and that isn't what you wanted, since you want them to progress through the form without being able to click on the tabs....But the nuts and bolts are here
DEMO HERE
$(document).ready(function(){
//Progress bar calculation
function setProgress(){
var length = $tabs.length;
var index = ($('li').index($(".active")))+1;
var width = (index/length)*100;
$('#progressBar').css('width', width+'%');
}
var $tabs = $('#tab_bar li');
setProgress();
$('#prevtab').on('click', function () {
$tabs.filter('.active').prev('li').find('a[data-toggle="tab"]').tab('show');
setProgress();
});
$('#nexttab').on('click', function () {
$tabs.filter('.active').next('li').find('a[data-toggle="tab"]').tab('show');
setProgress();
});
$tabs.click(function () {
setTimeout(setProgress, 200)
});
});
Then your HTML, I dunno what you had for < ul > , cuz you didn't provide that part... so I just hacked something up...but this should be similar to your structure
<ul class="nav nav-tabs" id="tab_bar">
<li class="active">Intro</li>
<li>SecondTab</li>
<li>Third</li>
<li>Fourth</li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="introduction">
asdasdasdas
</div>
<div class="tab-pane" id="secondtab">
asdasdsagreteryterythhgh
</div>
<div class="tab-pane" id="thirdtab">
rthrthrthrthrt
</div>
<div class="tab-pane" id="fourthtab">
yujghjuyjyujedgjhegj
</div>
</div>
<a class="btn" id="prevtab" type="button">Prev</button>
<a class="btn" id="nexttab" type="button">Next</button>
DEMO HERE