Use
npm, Bower
This package can be installed using npm
or Bower
by using either of the commands below. You'll then be able to use the assets from the provided directory.
# Via bower:
$ bower install --save bootstrap.native
# Via NPM:
$ npm install --save bootstrap.native
You can also create custom builds, check the README for more info.
RequireJS, CommonJS
The Native Javascript for Bootstrap library can be loaded easily via RequireJS
or CommonJS
, so if you are using a module loader, you can also use this library via
require()
as well. Here's how to do it:
// reference the library as dependency
var bsn = require("bootstrap.native");
// Create a button instance:
var btn = new bsn.Button(...);
Community contributors have written some more guidance on this topic, so make sure to check it.
Load from CDN
To load the library from CDN, drop one of these lines below before your ending </body>
tag. Latest releases come here on jsdelivr
and here on cdnjs. Other CDN links are also available.
<!-- jsdelivr repo -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/bootstrap.native/1.2.0/bootstrap-native.min.js"></script>
<!-- or cdnjs repo -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap.native/1.2.0/bootstrap-native.min.js"></script>
As most of the scripts manipulate the DOM when ready, there is no need to include a document.ready
like statement or link the script(s) in the <head>
. We don't do that
with native JavaScript unless required by a special ocasion.
Working locally
If you have to host the library in your project folders, download the package at github, unpack it, copy the
minified/un-minified file from dist/
folder into your application's assets folder, then simply drop this line (according to your application assets folders) before your ending
</body>
tag.
<script type="text/javascript" src="../assets/js/bootstrap-native.min.js"></script>
The /lib
folder contains the source for JavaScript components and cannot be used right away, they depend on the utility functions from the utils.js
file. You need to create
a custom build, so check npm/bower section below.
Additional styling
The dismissible Popover close button would require some adjustments for perfect looks. Copy this code in your application CSS files.
/* styles the close button for the dismissible popovers */
.popover .close { position: absolute; top: 7px; right: 10px; }
Previous versions of our library required some adjustments for other component, but since the original Bootstrap changed its CSS to work with CSS3 transitions, we also make use of them.
Browser support
The components are tested to work nearly perfect in all major browsers, and legacy browsers starting with IE8 (compatibility view OFF). Legacy browsers don't support CSS3 transitions and there is nothing we can/should do about that. In order to make legacy browsers comply with today's HTML5 standards, make sure you include a polyfill that covers most essential JavaScript API.
The Native JavaScript for Bootstrap comes with a dedicated 4Kb polyfill we made called polyfill.js for IE8-IE11. Since most
of the legacy browsers don't cover support for the standardized Event
the polyfill.js can be combined with HTML5 shims like so:
<script src="https://cdn.jsdelivr.net/bootstrap.native/1.2.0/polyfill.min.js"> </script>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<![endif]-->
Along with a set of corrections, the required polyfills are: classList
, indexOf
, Event
(including addEventListener
, CustomEvent
and dispatchEvent
) and have been packed with the above mentioned polyfill.js. Other viable solutions are the minifill.js
(which is mostly same as the provided polyfill) and the polyfill service by Financial Times:
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,getComputedStyle|gated"></script>
As you can see the scripts rely on very few unsupported features, so starting with 0.9.8 versions, some polyfill dependencies have been removed for better usability and reach. Any of the above choice should do, but apparently legacy browsers perform better with the provided polyfill, and the resons why the automated service renders legacy browsers slow is still unknown.
Remember that the polyfill link(s) must be in your <head>
, especially the HTML5 shims, so that the legacy browsers can render the content properly. As for the provided polyfill,
you can leave it there for all the browsers; make sure you check this example page here.
Before we go
All components are initialized right away, including Tooltip and Popover, as long as you use properly their DATA API attributes, but Button is an exception with its state/text switching function that needs to be triggered manually, like the example shown here.
The library uses a set of utilities required by the components, like a selector utility, CSS class checking and manipulation or event handling. This is why you cannot use the files in the
/lib
folder right away.
Some features have been dropped since I personally never found any use of them and/or Bootstrap developers decided to push them away. For instance we don't make use of any functions
to detect transitionend
, for performance and other reasons, we all know a fade is 150ms or Carousel default transition duration is 600ms, so most components have a simple
option for that. The duration option in most components might be useful for more advanced JavaScript driven animations.
Some features are even better, like having a paused
class for Carousel when in paused state, or an option to keep a Dropdown open as if you would have a form
inside, or Tooltip and Popover automatic (re)positioning without any required option and more. Additionally, all elements that the components use to initialize will store the initialization
object, giving you full access even for instances when the DATA API was used. EG: myModalTrigger.Modal
.
Namespace is probably the most important part to be aware of. While the original events like show.bs.modal
are inline with the original components exactly, some method names
might not be the same with the original library.
Don't use Modernizr or respond.js, they both make legacy browsers unusable. To target specific browsers, there are plenty of options. With this library you only need to make sure your layout looks consistent in legacy browsers, so you might consider browser specific classes to your HTML document, as show here. You don't have to worry about the JavaScript part.
About that respond.js thing, here is a 2k file of CSS to make your site's layout look normal on IE8. Why? Because CSS is still faster and better in most cases, while the JavaScript will heavily handle the browser's resize event, making it unresponsive and unusable. Open up this page on IE8 and let it fly.
Components
Modal
The Modal component works exactly as the original in most regards, with minor exceptions on options, events and public methods. It provides both an initialization option and a public method to write directly into your modal content.
In addition, a unique new ability that grants you access to the initialization even if your modal is setup via DATA API.
Options
Bootstrap 4 will remove the remote
option and we already replaced it with a better option. We also skipped the show
option because via JavaScript we quickly and easily access
the component's public methods right after initialiation, we'll have a look in a minute.
Name | type | default | description |
---|---|---|---|
backdrop |
boolean or the string 'static' |
true | Includes a modal-backdrop element. Alternatively, specify 'static' for a backdrop which doesn't close the modal on click. |
keyboard |
boolean | true | Option to dismiss the current modal via Esc key. |
duration |
number | 300 | If you are customizing the animation duration for Modal, you need to provide a duration value via JavaScript or data-duration="DURATION" attribute, an option to
make sure everything is in sync. |
content |
markup | The Modal component comes with a template system instead of a load remote content function (Bootstrap 4 drops it). This option can be used with JavaScript only. |
The default options' values are same as their jQuery plugin equivalents so you can expect same behavior.
Methods
For further control the Modal component exposes a couple of public methods to be used via JavaScript :
.show()
The method that shows an initialized modal. When called, it will also hide any other visible modal before showing the one requested, making sure to keep the backdrop in place.
.hide()
This hides an initialized modal. Additionally it will also close (if enabled) the backdrop.
.toggle()
When called it shows the modal if hidden and hides it otherwise, using the above two methods.
.setContent()
The method to enable you to set/override the content of <div class="modal-content">
element of your modal at any time, but you might want to avoid
using this method while the modal is animating.
.update()
This allows you to update the modal layout (handling overflowing/non-overflowing body and/or modal) after you have changed it's content or other layout changes occured.
This would naturally follow the previous .setContent()
method.
Events
Event Type | Description |
---|---|
show.bs.modal |
This event fires immediately when the .show() instance method is called. If caused by a click and the clicked element is a modal triggering element, that element is available
as the event.relatedTarget property of the event. |
shown.bs.modal |
This event is fired when the modal has been made visible to the user (set by our duration option). The event.relatedTarget is same as for the above. |
hide.bs.modal |
This event is fired immediately when the .hide() instance method has been called. |
hidden.bs.modal |
This event is fired when the modal has finished being hidden from the user (set by our duration option). |
The loaded.bs.modal
original event is not needed, also because we replaced the remote
option with another one. If modal is opened via JavaScript methods, or by clicking
on another element that is not a modal triggering element, the relatedTarget
is null.
Usage
Via DATA API
You can initialize Modal without writing any code as long as you have a modal and a trigger with data-target
or a link with href
referencing that modal. The component
will initialize for all elements with data-toggle="modal"
found in the DOM.
<!-- provide a trigger button -->
<button id="myModalTrigger" type="button" data-toggle="modal" data-target="#myModal">Launch modal</button>
<!-- Alternatively provide a link -->
<a id="myModalTrigger" data-toggle="modal" href="#myModal">Launch modal</a>
<!-- also the modal itself -->
<div id="myModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body">
Some content
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
The DATA API is suited for static content dialogs.
Via JavaScript
When you write your code, first make sure a modal template is present in the DOM and one or more triggering buttons that reference that modal. Via JavaScript initialization the
data-toggle="modal"
is not required, so you can grab a triggering element and initiate Modal, then immediately get access to methods. The JavaScript way is the only
way to deal with dynamically added modals.
The original plugin will initialize for the modal element, but we initialize for the triggering element, and this has it's own advantages and we'll disect that in a second. Let's create a very basic modal template for the record.
<!-- blank modal template -->
<div id="modalID" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<!-- the .setContent() method will update this element's HTML -->
</div>
</div>
</div>
Now let's initialize a triggering button element that targets the above modal via data-target="#myModal"
or href="#myModal"
.
// get a certain button trigger
var myModalTriggerButton = document.getElementById('myModalTrigger');
// initiate Modal with all options
// Note: options object is optional
var myModalInstance = new Modal(myModalTriggerButton,
{ // options object
content: '<div class="modal-body">Some content to be set on init</div>', // sets modal content
backdrop: 'static', // we don't want to dismiss Modal when Modal or backdrop is the click event target
keyboard: false, // we don't want to dismiss Modal on pressing Esc key
duration: 700, // let's assume we changed the animation duration for Modal
});
// OR initialize and show the modal right away
var myModalInstance = new Modal(myModalTriggerButton, options).show();
// now you know why we don't need a show option
Now we have an initialization reference in myModalInstance
, we can start applying the public methods.
// show the modal at any time
myModalInstance.show();
// hide the modal
myModalInstance.hide();
// toggle the modal (show/hide)
myModalInstance.toggle();
// change the modal content
myModalInstance.setContent('<div class="modal-body">Some different content</div>');
// if the above method is used while modal was shown, you can then ask for a layout update
myModalInstance.update();
After initialization via DATA API or JavaScript, we also have access to the component's original events.
// GET THE EVENT TARGET, THE MODAL
// when we are certain which modal ID to work with
var myModal = document.getElementById('myModal');
// also button trigger related (especially when modals are triggered by multiple instances)
// the triggering element is a link
var myModal = document.getElementById(myModalTriggerButton.getAttribute('href').replace('#',''));
// OR triggering element is not a link
var myModal = document.getElementById(myModalTriggerButton.getAttribute('data-target').replace('#',''));
// ATTACH HANDLERS
// show.bs.modal event
myModal.addEventListener('show.bs.modal', function(event){
// do something when this event triggers
// event.target is the modal referenced in myModal
// event.relatedTarget is the button referenced with myModalTriggerButton
}, false);
// shown.bs.modal event
myModal.addEventListener('shown.bs.modal', function(event){
// do something when this event triggers
// event.target is the modal referenced in myModal
// event.relatedTarget is the button referenced with myModalTriggerButton
}, false);
// hide.bs.modal event
myModal.addEventListener('hide.bs.modal', function(event){
// do something when this event triggers
// event.target is the modal referenced in myModal
}, false);
// hidden.bs.modal event
myModal.addEventListener('hidden.bs.modal', function(event){
// do something when this event triggers
// event.target is the modal referenced in myModal
}, false);
Additionally the component will store in the modal and it's triggering elements some references, for the internal execution, hopefully would help you as well:
// modal knows which element was clicked to execute the .show() method
var lastModalTrigger = myModal.modalTrigger;
// for modals with multiple triggering elements, the value changes every time the triggering element was clicked
Also each triggering element holds the initialization of the Modal component:
// assuming we a modal was initialized via DATA API
var myModalInstance === document.getElementById('myModalTrigger').Modal;
These references are used internally to hide currently visible modals when showing another one, and this is the reason why we initialize on the triggering element and not the modal, as a modal cannot hold multiple initialization instances for each triggering element.
Examples
Using the DATA API
The first example is a modal with static content initialized via DATA API, exactly as described in the above Use via DATA API section, and showcasing the ability to show another modal from a modal currently visible.
Via JavaScript
The following examples are focused on everything the Modal component offers for JavaScript initialization and usage. Given a modal template and some triggering elements that target the modal, let's initialize them one by one:
// we grab the trigger button by ID
var btnModal = document.getElementById('modalTriggerJS');
// set a custom content
var firstModalContent = '<div class="modal-header">'
+'<button type="button" class="close" data-dismiss="modal" aria-label="Close">'
+'<span aria-hidden="true">×</span>'
+'</button>'
+'<h4 class="modal-title" id="myModalJSLabel">Modal title</h4>'
+'</div>'
+'<div class="modal-body">'
+'<p>This is where you fill up content you know, etc.</p>'
+'</div>'
+'<div class="modal-footer">'
+'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>'
+'</div>';
// initialize Modal for this triggering element
var modalInitJS = new Modal(btnModal, {
content: firstModalContent,
backdrop: 'static'
});
// OR initialize with no options provided
// the options obect is optional
var modalInitJS = new Modal(btnModal);
Here is what the above code does:
Next we will initialize another triggering button, and attach a handler to it to change content of the modal when clicked.
// the reiggering element
var btnModal2 = document.getElementById('modalTriggerJS2');
// set some custom content or get if from external sources
var externalModalContent = {
title: 'A modal title',
content: 'Modal content to fill the modal-body.',
};
// set a custom modal-content template
var secondModalContent =
'<div class="modal-header">'
+'<button type="button" class="close" data-dismiss="modal" aria-label="Close">'
+'<span aria-hidden="true">×</span>'
+'</button>'
+'<h4 class="modal-title" id="gridModalLabel">' + externalModalContent.title + '</h4>'
+'</div>'
+'<div class="modal-body">'
+'<p>' + externalModalContent.content + '</p>'
+'</div>'
+'<div class="modal-footer">'
+'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>'
+'</div>';
// initialize Modal for this triggering element
var modalInitJS2 = new Modal(btnModal2, { backdrop: 'static' });
// now when we click this modal triggering element, we change the modal content
btnModal2.addEventListener('click', function() {
modalInitJS2.setContent(secondModalContent);
}, false);
By changing the innerHTML
of the modal-header
, modal-body
or modal-footer
with variables, you can achieve exactly the same as the
other examples from the demo of the original plugin. So we use same modal, but with different content:
Now if you go back to the previous triggering button and click it, you will notice that the modal is still like it was set by the second triggering button. Let's reset the modal content:
// we grab the button by ID
var btnModalNotTrigger = document.getElementById('modalNotTriggerJS');
// simply attach a click handler to it
btnModalNotTrigger.addEventListener('click', function() {
modalInitJS.setContent(firstModalContent); // revert modal content back to previous
modalInitJS.show(); // also show the modal
}, false);
Back to the previous modal content:
Another example is using the .update()
method. Let's say we have a modal initialized via DATA API, you might think we cannot access this instance's methods. Remember the trigger button
stores the initialization?
var modalUpdate = document.getElementById('modalUpdate'), // the trigger
anotherStaticModal = document.getElementById('anotherStaticModal'), // the modal
currentStaticModalBody = anotherStaticModal.querySelector('.modal-body'), // the body of the current modal
currentStaticModalBodyContent = currentStaticModalBody.innerHTML, // we cache the content of the body
modalUpdateInit = modalUpdate.Modal, // the initialization
changeModal1 = document.getElementById('changeModal1'), // the change buttons
changeModal2 = document.getElementById('changeModal2');
changeModal1.addEventListener('click', function(){
currentStaticModalBody.innerHTML = currentStaticModalBodyContent;
modalUpdateInit.update();
}, false);
changeModal2.addEventListener('click', function(){
currentStaticModalBody.innerHTML = '<h4>This modal changed via JavaScript</h4>';
currentStaticModalBody.innerHTML += '<p>Something you would want to be displayed in the body.</p>';
modalUpdateInit.update();
}, false);
A quick demo to showcase the above script:
Dropdown
The Dropdown component works like the original jQuery plugin and offers an additional option and an ability to handle click event in a way that you can create nested dropdowns and other cool stuff with ease.
The component also allows you to close the dropdown by pressing the Esc key and grants access to methods and original events.
Options
Name | type | default | description |
---|---|---|---|
persist |
boolean | false | Option to keep the dropdown-menu open when click event targets a child element, useful for forms or interactive content. |
Methods
.toggle()
For a given initialization the method shows the dropdown-menu if hidden or hides it otherwise.
Events
Just like the original jQuery plugin, the Dropdown component covers this part as well:
Event Type | Description |
---|---|
show.bs.dropdown |
This event fires immediately when the show instance method is called. |
shown.bs.dropdown |
This event is fired when the dropdown has been made visible to the user. |
hide.bs.dropdown |
This event is fired immediately when the hide instance method has been called. |
hidden.bs.dropdown |
This event is fired when the dropdown has finished being hidden from the user. |
The target of all component events is the parent <div class="dropdown">
, while the event.relatedTarget
is the triggering element with the specific
data-toggle="dropdown"
attribute. If you dismiss the dropdown via JavaScript, the Esc key or by clicking outside the triggering button, the event.relatedTarget
is null.
Usage
Via DATA API
Notice we use same markup as the original example, the triggering element with the data-toggle="dropdown"
attribute will initialize the Dropdown component.
<!-- basic dropdown template -->
<div class="dropdown">
<button id="myDropdown" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown trigger
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="myDropdown">
...
</ul>
</div>
Via JavaScript
When you want full control or you need a way to handle dynamically generated dropdown-menus, you can do it via JavaScript. Considering the above HTML was inserted into the DOM, let's initialize it real quick:
// get some element that has the data-toggle="dropdown"
var myDropdown = document.getElementById('myDropdown');
// initialize component with it's option
var myDropdownInit = new Dropdown( myDropdown, { persist: true } );
// OR initialize component with no option
// the options object is optional
var myDropdownInit = new Dropdown( myDropdown );
All right, now we have an initialization reference, we can get to work with the component's method:
// assuming the above, we can
myDropdownInit.toggle();
// or we can simply toggle the dropdown on initialization
var newDropdownInitialization = new Dropdown(myDropdown).toggle();
Also assuming the above HTML was used for your dropdown-menu, we can also attach the original events to the parent of the triggering element, which is the <div class="dropdown">
element:
// show.bs.dropdown
myDropdown.parentNode.addEventListener('show.bs.dropdown', function(event){
// do something when the event fires
// event.target is the PARENT of the triggering element
// event.relatedTarget is the triggering element
}, false);
// shown.bs.dropdown
myDropdown.parentNode.addEventListener('shown.bs.dropdown', function(event){
// do something when the event fires
// event.target is the PARENT of the triggering element
// event.relatedTarget is the triggering element
}, false);
// hide.bs.dropdown
myDropdown.parentNode.addEventListener('hide.bs.dropdown', function(event){
// do something when the event fires
// event.target is the PARENT of the triggering element
}, false);
// hidden.bs.dropdown
myDropdown.parentNode.addEventListener('hide.bs.dropdown', function(event){
// do something when the event fires
// event.target is the PARENT of the triggering element
}, false);
When your dropdown-menus have a different structure and the myDropdown.parentNode
is NOT the <div class="dropdown">
element, you might consider a
getClosest()
function to find it so you can then attach the original events to it.
Now, the Dropdown component also stores the initialization object in the targeted elements, just as for the modal triggering buttons:
// get some element we know it was initialized
var myDropdown = document.getElementById('myDropdown');
// reference the initialization
var myDropdownInit = myDropdown.Dropdown;
// use the public method
myDropdownInit.toggle();
Also you can do a simple check for the existence of 'Dropdown' in myDropdown
element to make sure you don't go the wrong way.
Examples
Right out of the box, the above dropdown template is initialized via DATA-API right away.
Ok now let's insert a new dropdown-menu and initialize right after via JavaScript.
// let's do it via a click handler to the following element
var makeMeDropdown = document.getElementById('makeMeDropdown');
// let's say we have a dropdown template as the above
// you may also reference one that you created yourself
var myDropdownTemplate = '<div class="dropdown">a valid template</div>';
makeMeDropdown.addEventListener('click', function(e){
e.preventDefault();
// don't do this again
if ( !/\bdisabled/.test(makeMeDropdown.className) ){
// invalidate the makeMeDropdown
this.setAttribute('disabled',true);
this.className = 'btn btn-default disabled';
this.innerHTML = 'All done';
// we inject the dropdown
this.parentNode.innerHTML += myDropdownTemplate;
// or use appendChild when myDropdownTemplate is an Element instance
// this.parentNode.appendChild( myDropdownTemplate );
// get a reference to the new dropdown
var formDropdown = document.getElementById('formDropdown');
// initiate with option
var DropdownInit = new Dropdown(formDropdown, {persist: true});
// also attach Dropdown original events
formDropdown.parentNode.addEventListener('show.bs.dropdown', function(e){
console.log('Do something when the event fires');
}, false);
formDropdown.parentNode.addEventListener('shown.bs.dropdown', function(e){
console.log('Do something when the event fires');
}, false);
formDropdown.parentNode.addEventListener('hide.bs.dropdown', function(e){
console.log('Do something when the event fires');
}, false);
formDropdown.parentNode.addEventListener('hidden.bs.dropdown', function(e){
console.log('Do something when the event fires');
}, false);
}
}, false);
That's all there is to do with Dropdown initialization, let's check a quick demo.
A quick last example is with nested dropdown-menus, as the component will look for the child elements and will prevent itself from closing if the click target is a child item with data-toggle
attribute.
Scrollspy
The ScrollSpy component inherits the layout and other requirements from the original jQuery plugin and offers public methods, the specific original event, and can be used with JavaScript as well as via DATA API.
The component will initialize for each element with data-spy="scroll"
attribute, but will not work if the above requirements are not met or the anchors don't reference the containers accordingly.
Options
Unlike the original jQuery plugin, this component requires no option for offset
, the default value works just fine.
Name | type | default | description |
---|---|---|---|
target |
string or a reference |
element '#ID' or other reference |
The only option for ScrollSpy, is the target of the container with data-spy="scroll" attribute.EG: data-target="#myMenuID" |
We will talk later about why we don't need an offset
option.
Methods
.refresh()
When DOM layout changes occured without triggering a resize of your element, you will have this option to immediately update the status of your menu items.
Events
Event Type | Description |
---|---|
activate.bs.scrollspy |
This event fires whenever a new item was activated by the component. |
The event target is the element we initialized the component via JavaScript or the data-spy="scroll"
attribute. The newly activated menu item's link is the event.relatedTarget
for the event.
Usage
Via DATA API
To initialize ScrollSpy, remember the requirements from the original jQuery plugin. For a quick reference we need some special HTML markup in order to initialize and the appropriate styling for the element and it's containers.
<!-- the element we initialize ScrollSpy on -->
<div data-spy="scroll" data-target="#navbar-example" class="scrollspy-example">
<section id="one"> <!-- this is a ScrollSpy container -->
Valid HTML goes here
</section>
<section id="two">
<section id="twoone">
One level nested containers also apply
</section>
<section id="twotwo">
This is your second nested container
</section>
</section>
</div>
<!-- we need a target, any of the below elements with an ID will do -->
<nav id="nav-example"> <!-- we can also target it's parent as well -->
<ul id="navbar-example" class="nav nav-stacked"> <!-- this is our element's target -->
<li><a href="#one">One</a></li>
<li>
<a href="#two">Two</a>
<ul class="nav nav-stacked">
<li><a href="#twoone">Two One</a></li>
<li><a href="#twotwo">Two Two</a></li>
</ul>
</li>
</ul>
</nav>
We mentioned that we don't need the offset
option and the reason is that
we can set some additional styling to force ScrollSpy containers to have the exact height
as the sum of all child elements and / or child containers.
/* the element we initialize ScrollSpy on */
.scrollspy-example {
position: relative; /* required */
height: 150px; overflow: auto; /* required by case: height must be px based, and overflow: scroll/auto */
/* other styling */
}
/* element child containers */
section {
position: relative; /* required */
display: inline-block; width: 100%; /* not required, but recommended */
}
For instances of ScrollSpy when the designated element is not overflowing and the scroll target is the window (like the example with the side navigation of this page), the container nesting is, as you probably noticed, unlimited.
Via JavaScript
For full control and access to the component's features, coding the JavaScript part is a breeze. Assuming the above markup have been injected into the DOM and the CSS is set, let's initialize, apply the public method and attach handlers to the original event.
// the element we initialize ScrollSpy on
var myScrollSpyElement = document.getElementsByClassName('scrollspy-example')[0];
// let's give the initialization a JavaScript reference for the "target" option
var myScrollSpyTarget = document.getElementById('myScrollSpyTarget');
// initialize the ScrollSpy for this element
var myScrollSpyInit = new ScrollSpy(myScrollSpyElement, {
// set option
target = myScrollSpyTarget;
// alternativelly, provide a valid selector string
// EG: ".my-unique-class-name" or "#my-unique-ID"
})
If the initialization validates (the target
option is valid and the component links the element with it's target), we have access to the method and the original event.
// apply the public method after DOM changed
// a new element container and it's corresponding menu item have been injected into the DOM
myScrollSpyInit.refresh();
// attach an event handler
myScrollSpyElement.addEventListener('activate.bs.scrollspy', function(event){
// do some cool stuff
// event.target is myScrollSpyElement
// event.relatedTarget is the menu item link that triggered the event
}, false);
To get access to an initialization object regardless of how it was initialized, here's how to do it:
// grab an element we know it was initialized via DATA API
var myScrollSpy = document.getElementById('myScrollSpy');
// check if the element is already initialized
var myScrollSpyIsInitialized = 'ScrollSpy' in myScrollSpy;
// if the above is true
var myScrollSpyInit = myScrollSpy.ScrollSpy;
// play with the public method
myScrollSpyInit.refresh();
Now this makes alot more sense, especially when you expect full control and also want to make sure you don't attach event handlers multiple times for your elements.
Examples
According to the above Usage Guide let's initialize something similar via DATA API:
Tumblr farm
Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.
Carles aesthetic
Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard. Freegan beard aliqua cupidatat mcsweeney's vero. Cupidatat four loko nisi, ea helvetica nulla carles. Tattooed cosby sweater food truck, mcsweeney's quis non freegan vinyl. Lo-fi wes anderson +1 sartorial. Carles non aesthetic exercitation quis gentrify. Brooklyn adipisicing craft beer vice keytar deserunt.
More Items
one
Occaecat commodo aliqua delectus. Fap craft beer deserunt skateboard ea. Lomo bicycle rights adipisicing banh mi, velit ea sunt next level locavore single-origin coffee in magna veniam. High life id vinyl, echo park consequat quis aliquip banh mi pitchfork. Vero VHS est adipisicing. Consectetur nisi DIY minim messenger bag. Cred ex in, sustainable delectus consectetur fanny pack iphone.
two
In incididunt echo park, officia deserunt mcsweeney's proident master cleanse thundercats sapiente veniam. Excepteur VHS elit, proident shoreditch +1 biodiesel laborum craft beer. Single-origin coffee wayfarers irure four loko, cupidatat terry richardson master cleanse. Assumenda you probably haven't heard of them art party fanny pack, tattooed nulla cardigan tempor ad. Proident wolf nesciunt sartorial keffiyeh eu banh mi sustainable. Elit wolf voluptate, lo-fi ea portland before they sold out four loko. Locavore enim nostrud mlkshk brooklyn nesciunt.
three
Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.
Keytar twee blog, culpa messenger bag marfa whatever delectus food truck. Sapiente synth id assumenda. Locavore sed helvetica cliche irony, thundercats you probably haven't heard of them consequat hoodie gluten-free lo-fi fap aliquip. Labore elit placeat before they sold out, terry richardson proident brunch nesciunt quis cosby sweater pariatur keffiyeh ut helvetica artisan. Cardigan craft beer seitan readymade velit. VHS chambray laboris tempor veniam. Anim mollit minim commodo ullamco thundercats.
For this example the element itself is the target of the scroll
event, and above it the .nav
component as it's target, while for the other example in this page, the side navigation on the right side, the window
is the target of the scroll
event.
Tab
The Tab component covers all original jQuery plugin functionality and even comes with new features such as being able to work with any kind of navigation components in Bootstrap, or providing support for height animation as you can see in the example below.
The component can initialize both via JavaScript and the DATA API, covers the original events and exposes a specific public method, but in contrast to the original plugin it offers some options for you to play with.
Options
Name | type | default | description |
---|---|---|---|
duration |
number | 150 | An internal timing setting to emulate transitionend measured in miliseconds.When using a CSS3 animation duration other than the default value, set the option via JavaScript or the data-duration="DURATION" attribute. |
height |
boolean | false | Option to enable animation of the height of the .tab-content tabs container. Can be set via JavaScript or the data-height="true" attribute. |
When you set the second option to true
, the duration
option is much more important as it will prevent any kind of animation inconsistency. This functionality assumes you are using
the styling of the Collapse component, which means that the duration
must be set according to the transition-duration
style rule of the Collapse component.
Both the above options have no effect on legacy browsers with no support for CSS3 transitions.
Methods
.show()
The method to be used to switch to a certain tab of your choice via JavaScript. If that tab is already visible or the method is used while animation is running, the method will produce no effect.
Events
The component events will fire in the exact order shown in table below:
Event Type | Description |
---|---|
hide.bs.tab |
This event fires when a new tab is to be shown (and thus the previous active tab is to be hidden). The event.target is the current active tab, while event.relatedTarget
is the new soon-to-be-active tab. |
show.bs.tab |
This event fires on tab show, but before the new tab has been shown. The event.target is the tab next to become active and event.relatedTarget is the current
active tab (if available). |
hidden.bs.tab |
This event fires after a new tab is shown (and thus the previous active tab is hidden). The event.target is the tab that just became inactive and event.relatedTarget
is the new active tab. |
shown.bs.tab |
This event fires on tab show after a tab has been shown. The event.target is the new active tab and event.relatedTarget is the previous active tab (if available). |
Usage
Via DATA API
Here is a sample markup to showcase the usage of the component with the above mentioned methods. As you can see, each of the elements with the data-toggle="tab"
attribute are subject to the
Tab component initialization.
<!-- for better usage, wrap the tabs and contents -->
<div id="myTabsWrapper">
<!-- Nav tabs -->
<ul id="myTabs" class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#home" data-toggle="tab" data-height="true" data-duration="300" aria-controls="home" role="tab">Home</a>
</li>
<li role="presentation">
<a href="#profile" data-toggle="tab" data-height="true" data-duration="300" aria-controls="profile" role="tab">Profile</a>
</li>
<li role="presentation">
<a href="#messages" data-toggle="tab" data-height="true" data-duration="300" aria-controls="messages" role="tab">Messages</a>
</li>
<li role="presentation">
<a href="#settings" data-toggle="tab" data-height="true" data-duration="300" aria-controls="settings" role="tab">Settings</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="home">...</div>
<div role="tabpanel" class="tab-pane" id="profile">...</div>
<div role="tabpanel" class="tab-pane" id="messages">...</div>
<div role="tabpanel" class="tab-pane" id="settings">...</div>
</div>
</div>
Also don't forget that this functionality works on CSS3 enabled browsers with the Collapse styling in place.
Via JavaScript
Since the component will target a single element with / or without data-toggle="tab"
attribute, but at least it references a corresponding tab via href
or
data-target
, we will need to do a simple loop to initialize multiple elements. Assuming the above markup have been injected into the DOM, let's initialize, use the public
method and attach handlers to the original events.
// first, we reference the .nav component that holds all tabs
var myTabs = document.getElementById('myTabs');
// let's give the initialization a JavaScript reference for the "target" option
var myTabsCollection = myTabs.getElementsByTagName('A');
// initialize the component for all items in the collection
for (var i = 0; i < myTabsCollection.length; i++) {
new Tab(myTabsCollection[i], // our target
{ // our options
duration: 300,
height: true
});
}
If each item in the collection meets the expected markup and the tab it referencing is found, the initialization will then validate and give you immediate access to method.
// get last item from collection and reference it's initialization
var myLastTab = myTabsCollection[myTabsCollection.length-1];
var myLastTabInit = myLastTab.Tab;
// assuming the last tab is not active, we can show it
myLastTabInit.show();
// attach an event handler as well
myLastTab.addEventListener('show.bs.tab', function(event){
// do some cool stuff
// event.target is myLastTab
// event.relatedTarget is the previous active tab
}, false);
We could have also built an Object
/ Array
with the initialization objects, but that depends very much on your needs.
Example
OK now we're ready to put this component to the test. We'll use all Bootstrap .nav
components in the pool.
Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica.
Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.
Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR.
Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.
Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork.
Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.
This tab has all events attached. Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater.
Wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.
Tooltip
Unlike the original jQuery plugin, our Tooltip component will initialize right away all elements with the data-toggle="tooltip"
attribute. Additionally the component can do automatic
placement without any options required. At half the size of the original plugin, our component here covers most needed options, methods and original events.
Options
The component covers most important options, excluding some of the options featured in the original jQuery plugin such as a template system needed template
option, a selector
option for auto-initialization, or a trigger
option. The component works different but has it's own advantages.
Name | type | default | description |
---|---|---|---|
animation |
string | fade | Option to customize the component animation. If you are using a different animation other than fade , you can specify that via the data-animation="ANIMATION" attribute.
This will add an additional CSS class to the tooltip to enable a custom transition. |
placement |
string | top | Option to set a specific placement to top , bottom , left or right , relative to it's target. Can be set via both JavaScript and the
data-placement="POSITION" attribute. |
duration |
number | 150 | An internal timing setting to emulate transitionend measured in miliseconds. When using a CSS3 animation duration other than the default value, set the option via JavaScript
or the data-duration="DURATION" attribute. |
delay |
number | 100 | A short delay before hiding the tooltip. Can be set via JavaScript or the data-delay="DELAY" attribute. |
container |
selector or object |
<body> |
The container where your tooltips get appended to. This option is only for JavaScript initialization and is not fully implemented as the original. |
Methods
For full control the Tooltip component exposes a couple of public methods to be used via JavaScript:
.show()
The method shows an initialized tooltip. When the method is executed, it will always create a new tooltip and append it into your desired container.
.hide()
The method hides an initialized tooltip and remove it from it's container and also from the memory, as if you would automatically destroy it.
.toggle()
The method shows the tooltip if hidden and hides it otherwise, using the above two methods.
As you can see we don't use a .destroy()
method, the reason for that is explained above, so let's explain how this has it's own advantages: if you change the
data-original-title="Your new title"
attribute value, next time you mouseover
the element, our component will use the new value without having to reinitialize the element. Sweet!
Events
The component's original events are same as with the original jQuery Plugin, except inserted.bs.tooltip
, the way the component works makes that this event is not needed, as it would fire
on every instance of the .show()
method.
Event Type | Description |
---|---|
show.bs.tooltip |
This event fires immediately when the show instance method is called. |
shown.bs.tooltip |
This event is fired when the tooltip has been made visible to the user (set by the duration option). |
hide.bs.tooltip |
This event is fired immediately when the hide instance method has been called. |
hidden.bs.tooltip |
This event is fired when the tooltip has finished being hidden from the user (set by the duration option). |
Usage
Via DATA API
As mentioned before the component will initialize any element found to have the data-toggle="tooltip"
attribute and a title
attribute.
<!-- any regular link with data-toggle="tooltip" -->
<a href="https://google.com" title="Google" data-toggle="tooltip" data-duration="300">Google</a>
<!-- any SVG shape with data-toggle="tooltip" -->
<svg viewBox="0 0 80 34" width="80" height="34" xmlns="http://www.w3.org/2000/svg">
<rect data-toggle="tooltip" data-placement="top" data-delay="150" title="Demo Title for SVG" rx="5"></rect>
</svg>
Via JavaScript
When you insert new items in the page and want them to initialize the component or you want to have full control over your tooltips, the JavaScript way is the best one. You can also initialize for
elements not having the specific DATA API, but at least have a title="Not null title"
attribute. You can do the following:
// find all elements with title attribute
var elementsTooltip = document.querySelectorAll('[title]');
// attach a tooltip for each
for (var i = 0; i < elementsTooltip.length; i++){
new Tooltip(elementsTooltip[i], {
placement: 'top', //string
animation: 'slideNfade', // CSS class
duration: 200, // integer
delay: 150, // integer
})
}
In addition, similar to any other component of this library, you can access the initialization and the public methods even for elements initialized via DATA API.
// find an element initialized with Tooltip
var myLinkWithTooltip = document.getElementById('myLinkWithTooltip');
// reference the initialization object
var myTooltipInit = myLinkWithTooltip.Tooltip;
Considering the just above element, let's go ahead and put the component's events to use:
// show.bs.tooltip
myLinkWithTooltip.addEventListener('show.bs.tooltip', function(event){
// do some cool stuff when .show() method is called
// event.target is myLinkWithTooltip
}, false);
// shown.bs.tooltip
myLinkWithTooltip.addEventListener('shown.bs.tooltip', function(event){
// do some cool stuff when .show() method completed
// event.target is myLinkWithTooltip
}, false);
// hide.bs.tooltip
myLinkWithTooltip.addEventListener('hide.bs.tooltip', function(event){
// do some cool stuff when .hide() method is called
// event.target is myLinkWithTooltip
}, false);
// hidden.bs.tooltip
myLinkWithTooltip.addEventListener('hidden.bs.tooltip', function(event){
// do some cool stuff when .hide() method completed
// event.target is myLinkWithTooltip
}, false);
Examples
Now let's test all the other placement positions, we start with inline links having the bottom placement, then left, and right. Let's put it to the test! Some heavy testing on the automatic repositioning with very very long tooltips.
Popover
Similar to the above, the Popover component will initialize all elements with the data-toggle="popover"
attribute. Unlike the original jQuery plugin, this component does not require
the Tooltip component and works just about the same as the above except that Popover has the ability to work with templates and trigger options.
Options
The component covers all needed options, including those for a template system:
Name | type | default | description |
---|---|---|---|
template |
markup | Option to use a custom HTML template via JavaScript only for your popovers. See examples below for info. | |
content |
markup | Option to set the content of the popover via JavaScript or the data-content="CONTENT" attribute. |
|
title |
markup | Option to set the title of the popover via JavaScript or the data-title="TITLE" attribute. |
|
dismissible |
boolean | false | Option to option to make the popover dismissible. When true, it will also add an X button at the top-right of the popover. You can enable this option via
data-dismissible="true" attribute. |
trigger |
string | hover | Option to change the component's action trigger event: hover, focus and click. In some cases you may want to open a popover on focus
for form elements or click for other buttons, you can specify that via JavaScript or the data-trigger="EVENT" attribute. |
animation |
string | fade | Option to customize the component animation. If you are using a different animation other than fade , you can specify that via the data-animation="ANIMATION"
attribute. This will add an additional CSS class to the popover to enable a custom transition. |
placement |
string | top | Option to set a specific placement to top , bottom , left or right , relative to it's target. Can be set via both JavaScript and the
data-placement="POSITION" attribute. |
duration |
number | 150 | An internal timing setting to emulate transitionend measured in miliseconds. When using a CSS3 animation duration other than the default value, set the option via
JavaScript or the data-duration="DURATION" attribute. |
delay |
number | 100 | A short delay before hiding the popover. Can be set via JavaScript or the data-delay="DELAY" attribute. |
container |
selector or object |
<body> |
The container where your popovers get appended to. This option is only for JavaScript initialization and is not fully implemented as the original. |
If a proper template is not specified via JavaScript or the content option is not set in any way, the Popover will not be initialized.
Methods
For full control the Popover component exposes a couple of public methods to be used via JavaScript:
.show()
The method shows an initialized popover. When the method is executed, it will always create a new popover and append it into your desired container.
.hide()
The method hides an initialized popover and remove it from it's container and also from the memory, as if you would automatically destroy it.
.toggle()
The method shows the popover if hidden and hides it otherwise, using the above two methods.
Also like the Tooltip component, there's no need for a .destroy()
method, for the same reason for that is explained before.
Events
The component's original events are same as with the original jQuery Plugin, except inserted.bs.popover
, just as explained for the Tooltip component.
Event Type | Description |
---|---|
show.bs.popover |
This event fires immediately when the show instance method is called. |
shown.bs.popover |
This event is fired when the popover has been made visible to the user (set by the duration option). |
hide.bs.popover |
This event is fired immediately when the hide instance method has been called. |
hidden.bs.popover |
This event is fired when the popover has finished being hidden from the user (set by the duration option). |
Usage
Via DATA API
So out component will initialize any element found to have the data-toggle="popover"
attribute and a data-content
attribute.
<!-- any regular link with data-toggle="popover" -->
<a href="https://google.com" data-title="Google" data-content="Google is cool" data-toggle="popover">Google</a>
<!-- any SVG shape with data-toggle="popover" -->
<svg viewBox="0 0 80 34" width="80" height="34" xmlns="http://www.w3.org/2000/svg">
<rect data-toggle="popover" data-placement="top" data-delay="150" data-content="Demo Title for SVG" rx="5"></rect>
</svg>
Via JavaScript
After inserting new content into the page, you can initialize any element with Popover via JavaScript. You can also initialize for elements not having the specific DATA API. You can do the following:
// find all elements with data-content attribute
var elementsPopover = document.querySelectorAll('[data-content]'); // also a certain class would go fine
// attach a popover for each
for (var i = 0; i < elementsPopover.length; i++){
new Popover(elementsPopover[i], {
placement: 'top', //string
animation: 'slideNfade', // CSS class
duration: 300, // integer
delay: 100, // integer
dismissible: true, // boolean
})
}
In addition, similar to any other component of this library, you can access the initialization and the public methods even for elements initialized via DATA API.
// find an element initialized with Popover
var myLinkWithPopover = document.getElementById('myLinkWithPopover');
// reference the initialization object
var myPopoverInit = myLinkWithPopover.Popover;
Considering the just above element, let's go ahead and put the component's events to use:
// show.bs.popover
myLinkWithPopover.addEventListener('show.bs.popover', function(event){
// do some cool stuff when .show() method is called
// event.target is myLinkWithPopover
}, false);
// shown.bs.popover
myLinkWithPopover.addEventListener('shown.bs.popover', function(event){
// do some cool stuff when .show() method completed
// event.target is myLinkWithPopover
}, false);
// hide.bs.popover
myLinkWithPopover.addEventListener('hide.bs.popover', function(event){
// do some cool stuff when .hide() method is called
// event.target is myLinkWithPopover
}, false);
// hidden.bs.popover
myLinkWithPopover.addEventListener('hidden.bs.popover', function(event){
// do some cool stuff when .hide() method completed
// event.target is myLinkWithPopover
}, false);
To use the template system, you can do the following:
//define some variables or get their values from other scripts
var someTitleFromOtherCode = 'Sample title';
var someContentFromOuterSpace = '<p>Some sample message.</p>';
//initiate Popover with the template
var popover2 = new Popover('.popover-via-template', { // where .popover-via-template is the text input
trigger: 'focus',
template: '<div class="popover" role="tooltip">'
+ '<div class="arrow"></div>'
+ '<h3 class="popover-title">'+someTitleFromOtherCode+'</h3>'
+ '<div class="popover-content">'+someContentFromOuterSpace+'</div>'
+ '</div>'
});
Examples
First let's test all the placement positions, we start with inline links having the bottom placement, then left, and right.
Now we are going to test buttons with a popover with large contents. The last two examples below are using the template system and different trigger options. The popover generated for the last two examples can be dismissed on window resize or blur (focus out).
Alert
The Alert component covers the specific original event and public method, but does not provide any option. Still, the component will initialize both via JavaScript or the DATA API. In
contrast with the original plugin is the fact that the component does not require the class alert-dismissible
in order to work.
Options
This component has no options, it uses a 150ms internal duration
to emulate transitionend
.
Methods
The Alert component exposes a single public method to be used via JavaScript:
.close()
The method hides an initialized alert and remove it from DOM.
Events
The component's original events are same as with the original jQuery Plugin.
Event Type | Description |
---|---|
close.bs.alert |
This event is fired immediately when the close instance method has been called. |
closed.bs.alert |
This event is fired when the alert has finished being hidden from the user. |
Usage
Via DATA API
The component will initialize all elements with proper DATA API found in the DOM. Note that the data-dismiss="alert"
attribute is required for the triggering button.
<!-- notice the <button> with the data-dismiss="alert" attribute -->
<div id="myWarningAlert" class="alert alert-warning alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
Some critical notice.
</div>
Via JavaScript
After inserting a new alert into the page, you can initialize it via JavaScript. Considering the above markup, you can do the following:
// find all elements with data-content attribute
var myWarningAlert = document.getElementById('myWarningAlert');
// initialize
var myWarningAlertInit = new Alert(myWarningAlert);
Also attach handlers to the original events:
// close.bs.alert
myWarningAlert.addEventListener('close.bs.alert', function(event){
// do something cool
// event.target is <div class="alert">
}, false);
// closed.bs.alert
myWarningAlert.addEventListener('closed.bs.alert', function(event){
// do something cool
// event.target is <div class="alert">
}, false);
Like all components of the library you can access the initialization object even if it was done via the DATA API:
// find an element initialized via DATA API
var myAlertButton = document.getElementById('myAlertButton');
// reference the initialization object
var myAlertInit = myAlertButton.Alert;
// apply the public method
myAlertInit.close();
Examples
This alert has some handlers attached to close.bs.alert
and closed.bs.alert
events, so check your console.
This alert uses the closed.bs.alert
event to show another alert.
Oh snap! You got an error!
If you close this alert, your PC will start formatting your drive.
Button
The Button component provides text/stateful switch and toggle checkboxes and radio buttons for button groups. The component provides a custom event for the button groups' toggling, not covered by the original jQuery Plugin.
Button doesn't cover single toggle feature. The supported toggle feature could very much fill the same purpose.
Collapse
The Collapse component covers the original events and methods of the jQuery plugin counterpart. This component understands there is a triggering element that finds its target collapsible element via the data-target="#collapse-id"
attribute or the href="#collapse-id"
attribute if it's a link.
Options
The options below allow you to connect a collapse to a parent accordion and a handy option to sync with the animation duration.
Name | type | default | description |
---|---|---|---|
parent |
selector or reference |
Option to reference a parent to be used as an accordion. When a parent is set and found, it will enable the functionality described in the show() method below. Can be set
via JavaScript or the data-parent="SELECTOR" attribute. |
|
duration |
number | 300 | Option the component's animation duration in miliseconds required to emulate transitionend . Can be set via JavaScript or the data-duration="DURATION" attribute. |
Methods
Calling any of the public methods while animation is running, will produce no effect.
.show()
The method will expand a collapsible element. In addition, if the collapsible element is part of an accordion (it's options include a reference to a parent), it will also close any other visible collapsible element.
.hide()
The method hides a collapsible element.
.toggle()
The method will show / or hide a collapsible element using the above methods and their full functionalities.
Events
All the component's events are attached to the collapsible element and not its targeting button / element, with other words, the event.target
is the element with the class="collapse"
attribute.
Event Type | Description |
---|---|
show.bs.collapse |
This event fires immediately when the show instance method is called. |
shown.bs.collapse |
This event is fired when a collapse element has been made visible to the user (set by the duration option). |
hide.bs.collapse |
This event is fired immediately when the hide method has been called. |
hidden.bs.collapse |
This event is fired when a collapse element has been hidden from the user (set by the duration option). |
Usage
Via DATA API
In the following markup, the component will initialize on the two .btn.btn-primary
elements with the data-toggle="collapse"
, both targeting the same collapsible element with specific atttributes.
<!-- toggle collapse via link with HREF reference -->
<a id="collapseLink" class="btn btn-primary" role="button" aria-expanded="false" aria-controls="collapseExample"
data-toggle="collapse" href="#collapseExample"> <!-- required DATA API -->
Link with href
</a>
<!-- AND / OR toggle collapse via button with data-target attribute reference -->
<button id="collapseButton" class="btn btn-primary" type="button" aria-expanded="false" aria-controls="collapseExample"
data-toggle="collapse" data-target="#collapseExample"> <!-- required DATA API -->
Button with data-target
</button>
<!-- and the basic collapsible template -->
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
Now if we stack multiple collapsible elements and wrap them into one parent with an ID attribute and some helper CSS classes, we can easily create an accordion.
<!-- accordion template -->
<div class="panel-group" id="myAccordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#myAccordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
Collapse CONTENT 1
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingTwo">
<h4 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#myAccordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Collapsible Group Item #2
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
Collapse CONTENT 2
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#myAccordion" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
Collapsible Group Item #3
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
<div class="panel-body">
Collapse CONTENT 3
</div>
</div>
</div>
</div>
Via JavaScript
If the above single collapse template have been inserted into the DOM, you need to initialize it via JavaScript.
// grab the accordion by its ID
var collapseLink = document.getElementById('collapseLink');
// initialize the component for this collapse trigger
var myCollapseInit = new Collapse(collapseLink, {
duration: 350 // integer
})
This now enables you to work with the public methods.
// call the show() right away
myCollapseInit.show();
// call the hide() later
// myCollapseInit.hide();
// OR call toggle() some other time
// myCollapseInit.toggle();
Also we can attach some handlers to the original events:
// first, we need to reference the collapsible element
var myCollapseExample = document.getElementById(collapseLink.getAttribute('href').replace('#',''));
// attach a handler to the `show.bs.collapse` original event
myCollapseExample.addEventListener('show.bs.collapse', function(event){
// do something cool when .show() method is called
// event.target is myCollapseExample
}, false);
Alright, now let's say the above accordion template have been inserted into the DOM, you need to initialize its collapsible elements right away via JavaScript.
// grab the accordion by its ID
var myAccordion = document.getElementById('myAccordion');
// grab the collapsible triggers for this accordion
var myAccordionTriggers = myAccordion.querySelectorAll('[data-toggle="collapse"]');
// initialize the component for each collapse trigger
for (var i = 0; i < myAccordionTriggers.length; i++){
new Collapse(myAccordionTriggers[i], {
parent: myAccordion, // this is the above defined object
duration: 350 // integer
});
}
The component grants access to the initialization even for instances where the DATA API was used.
// grab the collapse trigger initialized via DATA API
var myCollapseTrigger = document.getElementById('myCollapseTrigger');
// reference the initialization
var myCollapseTriggerInit = myCollapseTrigger.Collapse;
Examples
Single collapsible element
Here's a quick demo with a single collapsible element, using the .well
as the container, exactly as described in the Usage section. The demo also features the original events.
Accordion / multiple collapsible elements
Here's an Accordion example, built with a set of Panels wrapped in a <div class="panel-group">
element. When the toggle links are clicked, our Collapse component will look for the closest <div class="accordion-className">
or <div id="accordion-id">
via data-parent="selector"
and will hide any visible collapsible element.
Remember that all triggering buttons must reference the accordion via data-parent="selector"
as described above in order to collapse current opened collapsible element.
Carousel
The Carousel component covers the original events, along with a set of useful options and public methods. In addition it also provides a solid DATA API, it adds a paused
class to the targeted element when in paused state, and a solid event handling implementation.
Options
The options below allow you to connect a collapse to a parent accordion and a handy option to sync with the animation duration.
Name | type | default | description |
---|---|---|---|
keyboard |
boolean | true | Option that allows yout to navigate the carousel with left and right arrows. |
pause |
boolean or the text 'hover' |
'hover' | Option that makes possible to pause the carousel transition on mouse hover and touchdown. If you want to disable pause on hover, do that via JavaScript or the data-pause="false" attribute. |
interval |
number | 5000 | Option the component's delay between transitions in miliseconds. Can be set via JavaScript or the data-interval="INTERVAL" attribute. If you want to disable the automatic transition, you can set this option to false. |
duration |
number | 600 | Option the component's animation duration in miliseconds required to emulate transitionend . Can be set via JavaScript or the data-duration="DURATION" attribute. |
Methods
.cycle()
The method will cycle through items. Using the method while the animation is running will produce no effect.
.slideTo()
The method will allow you to jump to the index of a certain item. Using the method while the animation is running will produce no effect.
.getActiveIndex()
The method returns the index of the current active item.
Events
All the component's events are attached to the <div class="carousel">
element, and the event.relatedTarget
is the newly activated carousel item. The events provide no mention of the animation direction, because most polyfills do not allow us to set new custom properties to the event objects, we store the transition direction in the initialization object. See the Usage section.
Event Type | Description |
---|---|
slide.bs.carousel |
This event fires immediately when the slideTo() instance method is called. |
slid.bs.carousel |
This event is fired when transition has finished (set by the duration option). |
Usage
Via DATA API
The component covers most of the original implementation in regards to DATA API, except that you can ignore data-slide="prev"
and data-slide="next"
attributes for the controls, but they must have at least the class carousel-control
in order to work. The elements with the data-ride="carousel"
attribute will be initialized via DATA API, and the basic template looks like this:
<!-- the Carousel component -->
<div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="5000">
<!-- Indicators -->
<ol class="carousel-indicators">
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
<li data-target="#myCarousel" data-slide-to="1"></li>
<li data-target="#myCarousel" data-slide-to="2"></li>
</ol>
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<div class="item active">
<img src="https://unsplash.it/837/300?image=0" alt="">
<div class="carousel-caption">
<h3>This is another carousel</h3>
</div>
</div>
<div class="item">
<img src="https://unsplash.it/837/300?image=10" alt="">
<div class="carousel-caption">
<h3>This is a caption</h3>
</div>
</div>
<div class="item">
<img src="https://unsplash.it/837/300?image=210" alt="">
<div class="carousel-caption">
<h3>This is another caption</h3>
</div>
</div>
</div>
<!-- Controls -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
A quick walk through the attributes:
id="myCarousel"
is required only if you inject the above template into the DOM and need a way to target this specific element, unlike the original jQuery plugin that requires this attribute for the its internal event handlers;data-ride="carousel"
makes the element targetable by the Carousel component;data-interval="5000"
sets the automatic slide interval;data-slide-to="0"
when clicked, will slide to the carousel item with that index, 0 (zero) in this case;data-slide="prev"
/data-slide="next"
when clicked, will slide to the next / previous carousel item; these attributes are optional, but the classcarousel-control
is required;class="item active"
/class="active"
when active is present for an item or indicator, that indicates which carousel item is currently shown to the user, the above.getActiveIndex()
method will look for it when needed.
Via JavaScript
The component grants full access to the internal working via JavaScript; whether via the public methods or the original events, you can do a whole bunch of things. Assuming the above markup have been injected into the DOM, let's go ahead and initialize it:
// grab the carousel element
var myCarousel = document.getElementById('myCarousel');
// initialize with some options
var myCarouselInit = new Carousel(myCarousel, { // these options values will override the ones set via DATA API
duration: 700,
interval: false,
pause: false,
keyboard: false
});
And now we can play with the methods:
// use getActiveIndex()
var currentActiveItem = myCarouselInit.getActiveIndex();
// jump to a certain item
myCarouselInit.slideTo(2);
// if the carousel was set with `interval: false`
// we can do this to go to the next item
myCarouselInit.cycle();
As you probably expect by now, this component also stores the initialization in the element it targets on initialization, even for instances where the DATA API was used:
// get some carousel item and reference the initialization
var mySpecialCarouselInit = document.getElementById('mySpecialCarousel').Carousel;
// apply methods
mySpecialCarouselInit.cycle();
And now, instead of just writing some code sample on how to use the events, let's actually explain how the carousel at the top of this page works. With the help of the original events and some CSS, let's animate the contents of the items:
// grab the myCarousel on top of the page
var mainSlider = document.getElementById('myCarousel');
// also reference its items in an array
var mainSliderItems = mainSlider.querySelectorAll('.item');
// use the `slide.bs.carousel` event to remove the `slide` class
// FROM the div.carousel-caption of the newly activated item
mainSlider.addEventListener('slide.bs.carousel', function(e) {
// the Carousel compoenent also stores initialization in the targeted element object
var currentActiveIndex = mainSlider.Carousel.getActiveIndex();
var activeCaption = mainSliderItems[currentActiveIndex].querySelector('.carousel-caption');
activeCaption.classList.remove('slide');
});
// use the `slid.bs.carousel` event to add the `slide` class
// TO the div.carousel-caption of the newly activated item
mainSlider.addEventListener('slid.bs.carousel', function(e) {
// e.relatedTarget is the newly activated item
var activeCaption = e.relatedTarget.querySelector('.carousel-caption');
activeCaption.classList.add('slide');
});
To determine the slide direction, we simply access the initialization:
// read direction set by the last transition
var slideDirection = mainSlider.Carousel.direction;
// read it when `slide.bs.carousel` is triggered
mainSlider.addEventListener('slide.bs.carousel', function(e) {
slideDirection = mainSlider.Carousel.direction;
// now do something you need with slideDirection before transition
});
// read it when `slid.bs.carousel` is triggered
mainSlider.addEventListener('slid.bs.carousel', function(e) {
slideDirection = mainSlider.Carousel.direction;
// now do something you need with slideDirection after transition
});
Example
This is a test demonstrating the component capabilities and it's events, so open your console, and start clicking, you will be noticed before and after the animation. Also know that there was no active item set by default in the markup, proving the component can successfully manage this case by setting the first item as active on initialization.
Affix
The Affix component has it's own options and public method, but covers all the original events of the jQuery plugin. It inherits the same basic functionality as well as the CSS requirements to make it all work properly.
Options
Name | type | default | description |
---|---|---|---|
target |
selector or reference |
Option to pin an element to top once the scroll reaches it's target offsetTop. You can set the target via data-target=".target-className" or data-target="#target-ID" attribute. |
|
offsetTop |
number or function |
Option to pin an element to top when scroll reaches a specific value. This can be set via the data-offset-top="OFFSET" attribute, or via JavaScript you can assign a number or a function to the offsetTop option. |
|
offsetBottom |
number or function |
Option to pin an element to bottom at a certain offset relative to the window maximum scroll. This can be set via the data-offset-bottom="OFFSET" attribute, or via JavaScript you can assign a number or a function to the offsetBottom option. |
Using static values for offsetTop
and / or offsetBottom
would work best on non-responsive websites, or when the elements don't change their height when window resize occurs.
Methods
.update()
When DOM layout changes occured without triggering a resize in your context, you will have this option to immediately update the affix status of your element.
Events
Event Type | Description |
---|---|
affix.bs.affix |
This event fires immediately before the element has been affixed. |
affixed.bs.affix |
This event is fired after the element has been affixed. |
affix-top.bs.affix |
This event fires immediately before the element has been affixed-top. |
affixed-top.bs.affix |
This event is fired after the element has been affixed-top. |
affix-bottom.bs.affix |
This event fires immediately before the element has been affixed-bottom. |
affixed-bottom.bs.affix |
This event is fired after the element has been affixed-bottom. |
Usage
Required CSS
First we need to make sure our element is properly styled to make sure it works properly. For instance this is the style we needed for the navigation on the right side to make it work:
/* this style applies to mobile devices, on screens SMALLER than 768px */
#side-nav {clear: both}
#side-nav .nav.affix,
#side-nav .nav.affix-bottom { position: relative; margin: 20px 0; } /* relative position recommended */
/* this style applies to other devices, on screens LARGER than 768px */
@media (min-width: 768px) {
#side-nav .nav.affix, /* the fixed position and a width are required */
#side-nav .nav.affix-bottom { position: fixed !important; width: 263px; }
#side-nav .nav.affix { top: 0; } /* affixedTop position */
#side-nav .nav.affix-bottom { top: auto; bottom: 100px; } /* affixedBottom position */
}
In addition, via JavaScript you can ask the browser to update the value of width: 263px;
on window resize.
Via DATA API
Alright, we put the styling out of the way, we'll have a look at our example here on this very page. So we configure an element to be pinned to top targeting a certain element's position and a bottom offset of around 120px.
<ul id="myAffix" class="nav nav-stacked" data-spy="affix" data-target="#use" data-offset-bottom="120">
<!-- menu items -->
</ul>
If everything is set properly you should be able to achieve the same exact effect.
Via JavaScript
Assuming the above element has been injected into the page, you can initialize it via JavaScript:
// grab the element by it's ID
var myAffix = document.getElementById('myAffix');
// initialize
var theAffixInit = new Affix(myAffix, {
target: '#use',
offsetBottom: 120,
});
// use the public method any time you see fit
theAffixInit.update();
Also via JavaScript we can set offset options as functions:
// grab the element by it's ID
var myAffix = document.getElementById('myAffix');
// initialize with some cool options
var theAffixInit = new Affix(myAffix, {
offsetTop: function() {
var rectTop = document.getElementById('use').getBoundingClientRect().top;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return rectTop + scrollTop;
},
offsetBottom: function() {
var reasonableMargin = 20;
var elementHeight = myAffix.offsetHeight;
var footerHeight = document.getElementsByTagName('footer')[0].offsetHeight;
var maxScroll = Math.max(
document.body.scrollHeight,
document.body.offsetHeight,
document.documentElemnt.clientHeight,
document.documentElement.scrollHeight,
document.documentElement.offsetHeight );
return maxScroll - elementHeight - footerHeight - reasonableMargin;
},
});
Now let's make use of some of the component's original events:
// affix.bs.affix
myAffix.addEventListener('affix.bs.affix', function(event){
// do something when myAffix is about to be affixed top or bottom
// event.target is myAffix
}, false);
// affixed-top.bs.affix
myAffix.addEventListener('affixed-top.bs.affix', function(event){
// do something when myAffix is affixed top
// event.target is myAffix
}, false);
// and so on
If your element have been initialized by the component via DATA API, you can still access the initialization object:
// grab the element by it's ID
var myAffix = document.getElementById('myAffix');
// reference the initialization
var theAffixInit = myAffix.Affix;
// use the public method
theAffixInit.update();
Example
We don't have a working example for this component in this section, due to the complexity and requirements, however the navigation on the right side of this page is a good example, it covers initialization via DATA API, the required styling and the original events.
About
The Project
The Native JavaScript for Bootstrap project was born to help. It doesn't totally and perfectly replace all the functions the jQuery Plugins have for Bootstrap, but the essential tools. In many cases it performs much better than the original code, like an upgraded version of the original. At first sight, you wouldn't notice any difference with the demos of the original plugins.
I've literally learned how to code native JavaScript developing the library. The project aims to help me and you transition into a better developer, doing things right from the next big project. The work on maintenance and improvements over time focus on ways to improve overall code quality, performance and usability.
Why Native JavaScript?
Today's most popular HTML5 framework is Bootstrap and like any other there's ups and downs. We all know about the PROs, but one of the CONs is the jQuery dependency. Why in the world would you ever drop jQuery? What can you put in it's place? The answer is this this: clean JavaScript code. All browsers support it and the latest trends indicate that jQuery is becoming less relevant.
The standards are now generally adopted and mobile platforms are "on the wave". The future brings more changes and we cannot fix-n-hack or hack-to-fix anymore via jQuery. Performance is the one and only true goal to follow, so if dropping jQuery and cleaning up the code is the way to go, we'll go for it. jQuery is still so cool and efficient but I feel it's too overpowered with it's cross-browser solutions. With the new JavaScript engines and HTML5 standards, really, why not code native code, it's really not that hard.
Really Write Less, Do More?
Why do I feel this sounds much too bold after only a few weeks of vanilla JavaScript? With the given JavaScript here and some CSS customizations you can now do alot more than jQuery with the original Bootstrap plugins. As I have shown here, I think I've completely blasted the "do more" myth.
If you forgot to check performance metrics, go ahead (there are lots more out there, plus a ton of guides on how to boost performance), there is also the page load speed issue I forgot to mention. Imagine you can be drastically improve the page load by dropping jQuery.
If we compare the size of jQuery v1.11.2 + Bootstrap v3.3.5 minified is just about 93Kb + 36Kb = 129Kb. Our library here is (depending on your browser) 4Kb + 20Kb = 24Kb, where 4Kb is the size of provided polyfill for legacy browsers (IE8+), mostly under 5kb for most modern browsers via polyfill services. This library is only 6.6Kb gZipped!
This being said, jQuery doesn't make any sense for many of us.