Bootstrap 3 Nav Tabs on Top and Bottom of Page? - javascript

I'm building a site in Bootstrap 3 and have figured out that you can indeed place both a top nav-tab and bottom nav-tab section on the same page. These two separate tab menus will trigger the same tab content areas to show/hide.
Doing this works just fine with one problem. The tabs work independently of one another. I need the top and bottom tabs to show the same active tab. Right now if you click tab 4 on the top tab menu, it will take you to tab 4 but the bottom menu still shows the active tab as tab 1.
So, can Javascript/Jquery handle this issue? Just need them to act as the same menu.
Thanks in advance for any help.
<div class="tabbable tabs-below">
Should match top nav tabs.
http://jsfiddle.net/UT6ex/1/

Instead of listening to raw clicks, listen to the events triggered by the Bootstrap Tab class: show.bs.tab and shown.bs.tab. That will ensure future versions of Bootstrap markup won't break your setup.
This code finds <li> elements after a shown event to add and remove classes (working JSFiddle):
$('a[data-toggle="tab"]').on('shown.bs.tab',
function(event) {
var $relatedTarget, $target;
if (event.relatedTarget != null) {
$relatedTarget = $(event.relatedTarget);
$('a[data-toggle="' +
$relatedTarget.attr('data-toggle') +
'"][href="' +
$relatedTarget.attr('href') +
'"]').parent('li').removeClass('active');
}
if (event.target != null) {
$target = $(event.target);
$('a[data-toggle="' +
$target.attr('data-toggle') +
'"][href="' +
$target.attr('href') +
'"]').parent('li').addClass('active');
}
}
);

Sth like this is ok i suppose:
$('.nav-tabs').find('li').on('click', function(){
var location = $(this).find('a').attr('href'),
children = $('.nav-tabs').find('li'),
links = children.find('a');
links.each(function() {
var mi = $(this),
miHrefs = mi.attr("href"),
miParents = mi.closest('li');
miParents.removeClass('active');
if(miHrefs == location)
{
miParents.addClass("active");
}
});
})
Ok this works fine. Have a look here http://jsfiddle.net/UT6ex/4/.

Related

How to prevent the browser window from scrolling when scrolling to item in list on page?

I have a scrollable list of elements on the page, divided into sections. Each section is associated with a tab of the section name above the list. When I click on a particular Tab, the list scrolls automatically to the section of the tabs name.
<Tab value="one" active={current === 'one'} onClick={() => handlerScroll(sect1, 'one')}>
Section1
</Tab>
This is how Tab's press is processed and the scroll occurs - I change the state to the selected value, and I scroll through the list using scrollIntoView to the beginning of the selected section, referring to the section through ref:
сonst [current, setCurrent] = React.useState('one');
const handlerScroll = (tab, current) => {
setCurrent(current);
tab.current.scrollIntoView({ block: "start", behavior: "smooth" });
};
The first argument is ref to the clicked section of the list:
const sect1 = useRef();
<div ref={sect1}>
</div>
The scroll works. BUT, when i clicked, the browser window "scrolls" to the top of the list (the page literally descends to the top border of the div, where the entire list is located), and only then the list itself scroll to the clicked section.
Is it possible to somehow disable the scrolling of the browser window, so that the page itself does not move anywhere, but only the contents of the list change and scroll when you press Tab?
scrollIntoView will scroll the whole page to the target element.
I suggest you scroll the child element relative to its' parent element.
function scrollParentToChild(parent, child) {
// Where is the parent on page
var parentRect = parent.getBoundingClientRect();
// What can you see?
var parentViewableArea = {
height: parent.clientHeight,
width: parent.clientWidth
};
// Where is the child
var childRect = child.getBoundingClientRect();
// Is the child viewable?
var isViewable = (childRect.top >= parentRect.top) && (childRect.bottom <= parentRect.top + parentViewableArea.height);
// if you can't see the child try to scroll parent
if (!isViewable) {
// Should we scroll using top or bottom? Find the smaller ABS adjustment
const scrollTop = childRect.top - parentRect.top;
const scrollBot = childRect.bottom - parentRect.bottom;
if (Math.abs(scrollTop) < Math.abs(scrollBot)) {
// we're near the top of the list
parent.scrollTop += scrollTop;
} else {
// we're near the bottom of the list
parent.scrollTop += scrollBot;
}
}
}
And you can achieve the smooth behavior with
.parent {
scroll-behavior: smooth;
}
Another solution you can use scrollintoviewifneeded instead of scrollIntoView but it's not supported on Firefox and IE at all.
Resource

Dropdowm menu in Vanilla JavaScript - for unknown number of buttons

please, can you give me a hint with Vanilla JS?
I have dropdown menu opening via button click and have 2 issues:
1) Dropdown is opening by clicking on button with unique ID. I need to get it working on Class name, because it have to be working on multiple buttons - and the number of them is unknown (they will load from REST API).
In jQuery is it working, but I need it in Vanilla JS.
If I try to select button by Class name, it will return array of buttons, but I don't know how to select from array, which button was being clicked on.
2) Dropdown menu is opening only on the second click on the button (and then it is toggling like it should), but the first click doesn't do anything.
My code is here:
// select Button - now by ID - but I need unknown number of buttons - from REST API - and the code working for all of them
var btn = document.getElementById("dropBtn1");
// select Dropdown menu - next to the button - to be sure it will open the right menu no matter which button will be pressed
var menu = btn.nextSibling;
while(menu && menu.nodeType != 1) {
menu = menu.nextSibling
}
//toggle dropdown menu open/close
btn.addEventListener("click", function() {
if (menu.style.display == 'none') {
menu.style.display = 'block';
}
else {
menu.style.display = 'none';
}
});
And working prototype is here on Codepen:
https://codepen.io/vlastapolach/pen/EXdLMy
Please, do you have any ideas how to fix this?
Thank you!
Quite easy, you need a general function that works on the context (=this):
//toggle dropdown menu open/close
function toggle() {
var btn=this;
var menu = btn.nextSibling;
while(menu && menu.nodeType != 1) {
menu = menu.nextSibling
}
if(!menu) return;
if (menu.style.display != 'block') {//fix 2)
menu.style.display = 'block';
} else {
menu.style.display = 'none';
}
});
Now you can assign this functions as an event handler onto all your elements:
window.addEventListener("DOMContentLoaded",function(){
document.querySelectorAll(".sth").forEach(function(btn){
btn.addEventListener("click",toggle,true);
});
});
Note that NodeList.forEach is quite new, may use [].slice to create a real array...
And you need to assign the Handlers to the newly added elements manually, or you need to listen on window and trace back the target:
window.onclick=function(event){
if(event.target.classList.contains("sth")){
toggle.call(event.target);
}
};

Make current div change relevant menu tab to active

In my mobile project I can press a menu tab (Tab2) and the tab becomes active (with background color for the active class) and the corresponding div (Div2) below will appear on the screen.
If I press another tab, that tab gets active with corr. div etc...all in all, works perfect!
But now I have the opposite problem, I want to press (in my case, swipe) a div onto my screen and have the corresponding tab above to be active (with color). e.g. swipe in Div1 on screen, Tab1 should be active and have a certain background color. Swipe in Div2, then Tab2 should be active instead etc. but can't get it to work.
Below are my li tags:
<ul class="nav nav-pills mobileNavbar">
<li onclick="swipeFunction(0)" class="col-xs-4 tab1" data-index="0">STREAM</li>
<li onclick="swipeFunction(1)" class="col-xs-4 tab2" data-index="1">CHAT</li>
<li onclick="swipeFunction(2)" class="col-xs-4 tab3" data-index="2">IDE</li>
</ul>
And my jQuery:
$('#carousel-example-generic').on('slid.bs.carousel', function() {
var savedIndex = $(this).find('.active').index();
if (savedIndex === 0){
// I want Tab1 to be active here
} else if (savedIndex == 1){
// I want Tab2 to be active here
} else if (savedIndex == 2){
// I want Tab3 to be active here
}
});
For more details into what I'm trying to build, incl. libs, images (and my previous problem, solved by "Pieter"):
Link to a section on horizontal web page
As I commented above, you are doing same thing in all the conditions. All you need to do is, to activate the navigation item by index you are getting from carousel. Try this:
$('#carousel-example-generic').on('slid.bs.carousel', function () {
var savedIndex = $(this).find('.active').index();
$('.nav-pills>li:eq(' + savedIndex + ')>a').css('background', 'yellow');
});
EDIT:
I would recommend to play with a CSS class instead of inline css. Try this:
$('#carousel-example-generic').on('slid.bs.carousel', function () {
var savedIndex = $(this).find('.active').index();
$('.nav-pills>li:eq(' + savedIndex + ')>a')
.addClass("active") // add active class to the current one
.parent().siblings() // select sibling navigation items
.removeClass("active"); // remove active class from the other navigations
});
You conveniently already have classnames "tab1", "tab2", "tab3" on the tabs, which can be matched easily based on savedIndex:
$('#carousel-example-generic').on('slid.bs.carousel', function() {
$('.nav li').removeClass('active'); // clear the old one
var savedIndex = $(this).find('.active').index();
$('.tab'+ (savedIndex+1)).addClass('active'); // add the new one
});
(I believe index() returns an integer, but if not you may need to parseInt it before adding 1.)

What is wrong with this code, trying a click menu?

So there is a left side vertical menu. And it has a small options button. When clicked, it should open a div which will have various filter options. Now, i need it to appear when clicked, and disappear when either the options button is clicked again, or the user clicks anywhere outside the div.
So i have the following code.
//options filter menu animation
$('#optionsmenu').hide(); //hides the menu
$('.optionsfilters').click(function(e){
var $this = $('#optionsmenu');
$this.show();
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
$this.css('top', top);
$this.css('left', left);
});
$(':not(.optionsfilters)').click(function(e){
$('#optionsmenu').hide();
});
The HTML is
<div id="sidebartitle">
<h2>Organisation</h2>
<a id="optionsfilters" class="optionsfilters">Options</a>
</div>
<div id="optionsmenu" class="optionsfilters">
<h3>Add New</h3>
<ul>
<label>Year</label>
<select>
<option>2000</option>
<option>2001</option>
<option>2002</option>
<option>2003</option>
<option>2004</option>
<option>2005</option>
</select>
</ul>
</div>
Its not working together, i.e. the two javascript functions, the first one works alone, when the second one is commented out. The second one works, if i comment out the hide part, and add an alert message. But together, they don't work.
Whats the conflict?
Ok there are a few things you will want to do:
1) If you want the original button to close the menu you will want to use .toggle() rather than .show()
2) You will want to detect a click on the document, to hide the options menu. Which will not be called if it is the options menu that is clicked due to the e.stopPropagation(); (point 4 below).
$(document).click(function() {
$('#optionsmenu').hide();
});
3) You also want to check (as both have the same class) that the .optionsfilters that was clicked was not the filters themselves (otherwise this will stop you clicking an option).
if( e.target !== this ) {
return;
}
4) Use e.stopPropagation(); to stop the event bubbling up to the parents (or document).
This should be what you are looking for:
$('#optionsmenu').hide(); //hides the menu
$('.optionsfilters').click(function(e){
e.stopPropagation();
if( e.target !== this ) {
return;
}
var $this = $('#optionsmenu');
$this.toggle();
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
$this.css('top', top);
$this.css('left', left);
});
$(document).click(function() {
$('#optionsmenu').hide();
});
Here is the working fiddle: http://jsfiddle.net/lee_gladding/pny4vq26/
Here you go
//options filter menu animation
var filters = $('.optionsfilters');
var options = $('#optionsmenu');
options.hide(); //hides the menu
filters.click(function(e){
$('#optionsmenu').toggle();
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
_this.css('top', top);
_this.css('left', left);
});
$(document).click(function(e) {
if (!filters.is(e.target) && filters.has(e.target).length === 0) {
options.hide();
}
});
http://jsfiddle.net/6e86hdwc/
a classic demonstration of stopPropagation in jQuery (http://api.jquery.com/event.stoppropagation/)
here is a fiddle that simplify this: http://jsfiddle.net/ymzrocks/98082xk7/1/
in short:
$('.optionsfilters').click(function(e)
{
e.stopPropagation(); // or elese it will fire the parent event
var $this = $('#optionsmenu');
$this.show();
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
$this.css('top', top);
$this.css('left', left);
});
The problem is mainly that both your click handlers run and one shows the menu then the other one hides it.
Why? Because that's not a good selector. Until you reach the element with the class .optionsfilters your click goes through body, #sidebartitle and then it reaches the element a. And since the parents of that element don't have the class .optionsfilters, it will hide the menu.
So I changed the code a little. First of all don't use the class as a click handler. Use the IDs
$('#optionsfilters').click(function (e) { // ID of options
var $this = $('#optionsmenu');
$this.toggle(); // toggle show/hide when click on it
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
$this.css('top', top);
$this.css('left', left);
});
The you need to check when you click outside of the #optionsmenu. For that you attach a click handler on document and check the e.target
$(document).click(function (e) {
if (!$(e.target).closest('.optionsfilters').length)
$('#optionsmenu').hide();
});
If the e.target itself is not .optionsfilters and is not a child of a parent with that class then you hide the menu.
Working example below.
//options filter menu animation
$('#optionsmenu').hide(); //hides the menu
$('#optionsfilters').click(function (e) {
var $this = $('#optionsmenu');
$this.toggle();
var left = $('#sidebar').css('width'),
top = $(this).offset().top;
$this.css('top', top);
$this.css('left', left);
});
$(document).click(function (e) {
if (!$(e.target).closest('.optionsfilters').length) $('#optionsmenu').hide();
});
#optionsmenu{
background:lightblue;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="sidebartitle">
<h2>Organisation</h2>
<a id="optionsfilters" class="optionsfilters">Options</a>
</div>
<div id="optionsmenu" class="optionsfilters">
<h3>Add New</h3>
<ul>
<label>Year</label>
<select>
<option>2000</option>
<option>2001</option>
<option>2002</option>
<option>2003</option>
<option>2004</option>
<option>2005</option>
</select>
</ul>
</div>
Be careful using :not for any other elements, because if - for example - you click the dropdown, the sidebar hides again, which I doubt you want to happen.
It'd be better to define areas you want to make the sidebar hide when clicked, like the main content area.
Also since you use the class .optionsfilters on your sidebar menu, when you click in there the sidebar will hide again.
Try this
JS Fiddle.
I can't understand, but if your menu is hide (first instruction in your js), how can you click on it for show it?
EDIT: Didn't noticed the class attriute on previous div. My fault.

How to change image with onclick while a list is using slideup and slidedown

I would like to change the src of an img when the parent of listed children is clicked. I am using some code I have been working with that I found on StackOverflow because I was having problems with slideup and slidedown.
Next to each uppermost item (parent), to the left will be an arrow icon pointing to the right. Upon clicking the icon, this image should change to an arrow pointing down.
I can get the image to change onClick, but unless you click on the image, the image does not change back. Therefore, I believe I need the change to be pegged to the slideup and slide down functions. The image should also change back if you click on the close link or when clicking on a new Parent.
I can live without the 'only one list can be shown at a time' functionality, which would eliminate the need for the image to also change on clicking a new parent.
For this fiddle, I have only applied what I was trying to do to the first parent of the list: http://jsfiddle.net/9aa5n/51/
HTML:
<li><img src="arrowright.png"></li>
<li class="show_hide" id="1C">
<p>lkjlkjasdfasdf</p>
Close
</li>
<li>Edit</li>
<li class="show_hide" id="2C">
<p>lkjlkjasdfasdf</p>
Close
</li>
<li>Edit</li>
<li class="show_hide" id="3C">
<p>lkjlkjasdfasdf</p>
Close
</li>
jQuery / Javascript:
$(document).ready(function() {
$('.show_hide').slideUp(0);
$('.edit_this').click(function() {
$('.show_hide').slideUp(300);
var takeID = $(this).attr('id');
$('#' + takeID + 'C').slideDown(300);
});
$('.close').click(function() {
var takeID = $(this).attr('id').replace('Close', '');
$('#' + takeID + 'C').slideUp(300);
});
});
$('#img-tag').on({
'click': function() {
var src = ($(this).attr('src') === 'arrowright.png')
? 'arrowdown.png'
: 'arrowright.png';
$(this).attr('src', src);
}
});
I updated your jsfiddle: http://jsfiddle.net/9aa5n/53/
Since you didn't provide absolute paths to images, I added some from the net.
I removed your click event, and replaced it with this, I believe your issue was how you were referencing the elements in jQuery.
$(".edit_button").click(function() {
var img = $(this).find("img").first();
console.log(img.attr("src"));
if (img.attr("src") === 'http://iconizer.net/files/Brightmix/orig/monotone_arrow_right.png') {
img.attr("src", 'http://png-3.findicons.com/files/icons/2315/default_icon/256/arrow_down.png');
console.log(img.attr("src"));
} else {
img.attr("src", 'http://iconizer.net/files/Brightmix/orig/monotone_arrow_right.png');
console.log(img.attr("src"));
}
});
This should get you started to finish polishing up the UI,
i.e. closing all .edit_button and then open only the $(this).find("img").first() element ...

Categories