From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
We have received a couple of requests on how to use the BookBlock plugin in fullscreen. So, we decided to create a fullscreen layout, apply BookBlock to it and add a sidebar menu. We’ll show you how to customize BookBlock and use some available options for navigating the content.
The idea is to navigate the pages using the arrows or swiping the pages, and to slide out the menu when clicking on the menu button. The sidebar menu will contain links to different pages, i.e. the table of contents. When clicking on a table of contents entry, we’ll jump to the respective page.
We’ll also use jScrollPane by Kevin Luck to add a custom scrollbar for the content when needed.
The demo contains excerpts form the hilarious “The Funny Side of Physic” by A. D. Crabtre from Project Gutenberg.
The following libraries and jQuery plugins will be used:
- BookBlock by Pedro Botelho
- Custom jQuery++ by Bitovi
- jScrollPane by Kevin Luck
- jQuery Mouse Wheel Plugin by Brandon Aaron
- Custom Modernizr (peek inside to see what this build includes)
So, let’s get started!
The Markup
Let’s have a main container for all our elements. Inside, we’ll add a division for the sidebar menu which we’ll give the class “menu-panel”, and the wrapper for the BookBlock with the class “bb-custom-wrapper”. The BookBlock will contain the wrapper (that we’ll apply the plugin to) and the structure that the plugin needs. Inside of each item, we’ll add a content wrapper with a division that we’ll need for the custom scroll functionality:
<div id="container" class="container"> <div class="menu-panel"> <h3>Table of Contents</h3> <ul id="menu-toc" class="menu-toc"> <li class="menu-toc-current"><a href="#item1">Self-destruction</a></li> <li><a href="#item2">Why we die</a></li> <li><a href="#item3">The honeymoon</a></li> <li><a href="#item4">A drawing joke</a></li> <li><a href="#item5">Commencing practice</a></li> </ul> </div> <div class="bb-custom-wrapper"> <div id="bb-bookblock" class="bb-bookblock"> <div class="bb-item" id="item1"> <div class="content"> <div class="scroller"> <h2>Self-destruction</h2> <p>...</p> </div> </div><!-- /content --> </div><!-- /bb-item --> <div class="bb-item" id="item2"><!-- ... --></div> <div class="bb-item" id="item3"><!-- ... --></div> <div class="bb-item" id="item4"><!-- ... --></div> <div class="bb-item" id="item5"><!-- ... --></div> </div><!-- /bb-bookblock --> <nav> <a id="bb-nav-prev" href="#">←</a> <a id="bb-nav-next" href="#">→</a> </nav> <span id="tblcontents" class="menu-button">Table of Contents</span> </div><!-- /bb-custom-wrapper --> </div><!-- /container -->
The menu items will point to the respective BookBlock pages (bb-item). We’ll also add navigation arrows and a button for toggling the opening and closing of the menu.
Let’s move on to the style.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The CSS
Note that we won’t use any vendor specific prefixes here, but you’ll find them in the files.
We don’t discuss the style of the BookBlock plugin here (you can find the styles in bookblock.css), instead we’ll focus on all the other styles that are important for the layout and customize a couple of things.
Let’s start by importing the Lato font from Google web fonts:
@import url(http://fonts.googleapis.com/css?family=Lato:300,400,700);
The html element should have a height of 100% since we will need to set the heights of absolute elements inside to expand to the window’s height:
html { height: 100%; }
Let’s use border-box for box-sizing which will allow us to define percentages for widths and heights of elements while using paddings, without worrying about oversizing elements:
*, *:after, *:before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; margin: 0; }
(We are brutally resetting the paddings in margins of all elements; you don’t have to do that, instead you could just normalize the respective elements.)
Let’s define the font for the body and set it’s height to 100%. Remember, we need to set it because of the children that will be absolute and 100% in height as well:
body { font-family: 'Lato', Calibri, Arial, sans-serif; font-weight: 400; font-size: 100%; color: #333; height: 100%; }
We are using Modernizr and we’ve added the class “no-js” to the html element. When JavaScript is enabled, Modernizr will replace that class with “js”. This will allow us to give certain CSS properties to elements that we don’t want if JavaScript is disabled. Our 100% width and height layout only makes sense if we have JS enabled and only then we want the body to have its overflow hidden:
.js body { overflow: hidden; }
Let’s define some link styles:
a { color: #555; text-decoration: none; } a:hover { color: #000; }
We want our main container to occupy all the window width and height. The sidebar menu will be positioned outside of it by setting a negative left value (of its own width). The idea is to animate the whole container when we click to open the menu. It will slide to the right revealing the overflown sidebar menu.
So, let’s set the main wrappers’ widths and heights to 100% and add a transition to the container:
.container, .bb-custom-wrapper, .bb-bookblock { width: 100%; height: 100%; } .container { position: relative; left: 0px; transition: left 0.3s ease-in-out; }
When we click on the menu button, we will add another class to the container which will set the left to 240 pixels (width of the sidebar menu):
.slideRight { left: 240px; }
Without JavaScript enabled, we won’t be able to do this, so let’s add a left padding instead:
.no-js .container { padding-left: 240px; }
By default, we want the sidebar menu to be fixed to the left side:
.menu-panel { background: #f1103a; width: 240px; height: 100%; position: fixed; z-index: 1000; top: 0; left: 0; text-shadow: 0 1px 1px rgba(0,0,0,0.1); }
When JS is enabled, we’ll set the position to absolute and the left to -240 pixel:
.js .menu-panel { position: absolute; left: -240px; }
Let’s style the elements of the menu:
.menu-panel h3 { font-size: 1.8em; padding: 20px; font-weight: 300; color: #fff; box-shadow: inset 0 -1px 0 rgba(0,0,0,0.05); } .menu-toc { list-style: none; } .menu-toc li a { display: block; color: #fff; font-size: 1.1em; line-height: 3.5; padding: 0 20px; cursor: pointer; background: #f1103a; border-bottom: 1px solid #dd1338; } .menu-toc li a:hover, .menu-toc li.menu-toc-current a{ background: #dd1338; }
The navigation will be positioned absolutely on top of everything:
.bb-custom-wrapper nav { top: 20px; left: 60px; position: absolute; z-index: 1000; }
The arrow links and the menu button will also be positioned absolutely and we’ll make them round by setting the border-radius to 50%:
.bb-custom-wrapper nav span, .menu-button { position: absolute; width: 32px; height: 32px; top: 0; left: 0; background: #f1103a; border-radius: 50%; color: #fff; line-height: 30px; text-align: center; speak: none; font-weight: bold; cursor: pointer; } .bb-custom-wrapper nav span:last-child { left: 40px; } .bb-custom-wrapper nav span:hover, .menu-button:hover { background: #000; }
The menu button will be positioned in the top left corner and we’ll hide its text:
.menu-button { z-index: 1000; left: 20px; top: 20px; text-indent: -9000px; }
Let’s create a little menu icon by using a pseudo-element with a double box shadow for the upper and lower line:
.menu-button:after { position: absolute; content: ''; width: 50%; height: 2px; background: #fff; top: 50%; margin-top: -1px; left: 25%; box-shadow: 0 -4px #fff, 0 4px #fff; }
In case that there is no JS enabled, we don’t need any of those elements, so we’ll simply hide them:
.no-js .bb-custom-wrapper nav span, .no-js .menu-button { display: none; }
Let’s move to the inner parts of the BookBlock items. The content division needs to be absolute and we’ll set the overflow to hidden. This is important because we want to apply our custom scroll here and we’ll only do that when a page was turned. If we wouldn’t set the overflow to hidden, we’d see the content overflowing. Again, this only makes sense when we have JS enabled, so we’ll add the “js” class:
.js .content { position: absolute; top: 60px; left: 0; bottom: 50px; width: 100%; overflow: hidden; }
The scroller div is the one that will grow with content, so let’s set some paddings here:
.scroller { padding: 10px 5% 10px 5%; }
Using percentages as lateral padding will make the layout adjust liquidly to the screen size.
Let’s hide those sharp edges when we scroll by adding pseudo-elements with a white to transparent gradient to the top and the bottom of the content div:
.js .content:before, .js .content:after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 20px; z-index: 100; pointer-events: none; background: linear-gradient( to bottom, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100% ); } .js .content:after { top: auto; bottom: 0; background: linear-gradient( to top, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100% ); }
This will make the text appear slightly faded out.
Let’s style the text elements:
.content h2 { font-weight: 300; font-size: 4em; padding: 0 0 10px; color: #333; margin: 0 1% 40px; text-align: left; box-shadow: 0 10px 0 rgba(0,0,0,0.02); text-shadow: 0 0 2px #fff; } .no-js .content h2 { padding: 40px 1% 20px; } .content p { font-size: 1.2em; line-height: 1.6; font-weight: 300; padding: 5px 8%; text-align: justify; }
Finally, let’s add some media queries. Without JavaScript enabled, we don’t want to show the menu anymore from 800 pixels on. This is just an example of how we can control these elements under certain conditions.
The last media query will resize the font a bit, for smaller devices. Using ems makes things easier for these cases.
@media screen and (max-width: 800px){ .no-js .menu-panel { display: none; } .no-js .container { padding: 0; } } @media screen and (max-width: 400px){ .menu-panel, .content { font-size: 75%; } }
And that was all the style! Let’s add some funky dance moves, shall we?
The JavaScript
We will start by caching some elements and initializing the BookBlock plugin. We need to set some things after each page flip, mainly the current item’s index and the jScrollPane behavior. This is specified in the onEndFlip callback passed to the BookBlock.
var $container = $( '#container' ), // the element we will apply the BookBlock plugin to $bookBlock = $( '#bb-bookblock' ), // the BookBlock items (bb-item) $items = $bookBlock.children(), // index of the current item current = 0, // initialize the BookBlock bb = $( '#bb-bookblock' ).bookblock( { speed : 800, perspective : 2000, shadowSides : 0.8, shadowFlip : 0.4, // after each flip... onEndFlip : function(old, page, isLimit) { // update the current value current = page; // update the selected item of the table of contents (TOC) updateTOC(); // show and/or hide the navigation arrows updateNavigation( isLimit ); // initialize the jScrollPane on the content div for the new item setJSP( 'init' ); // destroy jScrollPane on the content div for the old item setJSP( 'destroy', old ); } } ), // the navigation arrows $navNext = $( '#bb-nav-next' ), $navPrev = $( '#bb-nav-prev' ).hide(), // the table of content items $menuItems = $container.find( 'ul.menu-toc > li' ), // button to open the TOC $tblcontents = $( '#tblcontents' ), transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, // transition event name transEndEventName = transEndEventNames[Modernizr.prefixed('transition')], // check if transitions are supported supportTransitions = Modernizr.csstransitions;
First, let’s bind the events to some of the elements initialized before. Also, we need to initialize the jScrollPane for the first (current) item.
function init() { // initialize jScrollPane on the content div of the first item setJSP( 'init' ); initEvents(); }
Since we’ll eventually need to initialize, reinitialize and destroy the jScrollPane, let’s define a function for this:
function setJSP( action, idx ) { var idx = idx === undefined ? current : idx, $content = $items.eq( idx ).children( 'div.content' ), apiJSP = $content.data( 'jsp' ); if( action === 'init' && apiJSP === undefined ) { $content.jScrollPane({verticalGutter : 0, hideFocus : true }); } else if( action === 'reinit' && apiJSP !== undefined ) { apiJSP.reinitialise(); } else if( action === 'destroy' && apiJSP !== undefined ) { apiJSP.destroy(); } }
We will need to bind several events:
- We will call the BookBlock’s next() and prev() methods when we click the navigation arrows or when swiping the page
- The TOC will be shown / hidden when we click the $tblcontents (menu) button
- We will call the BookBlock jump() method when we click a TOC item
- The jScrollPane will be initialized on window resize
So, here we go:
function initEvents() { // add navigation events $navNext.on( 'click', function() { bb.next(); return false; } ); $navPrev.on( 'click', function() { bb.prev(); return false; } ); // add swipe events $items.on( { 'swipeleft' : function( event ) { if( $container.data( 'opened' ) ) { return false; } bb.next(); return false; }, 'swiperight' : function( event ) { if( $container.data( 'opened' ) ) { return false; } bb.prev(); return false; } } ); // show TOC $tblcontents.on( 'click', toggleTOC ); // click a menu item $menuItems.on( 'click', function() { var $el = $( this ), idx = $el.index(), jump = function() { bb.jump( idx + 1 ); }; current !== idx ? closeTOC( jump ) : closeTOC(); return false; } ); // reinit jScrollPane on window resize $( window ).on( 'debouncedresize', function() { // reinitialise jScrollPane on the content div setJSP( 'reinit' ); } ); }
The navigation arrows’ visibility will depend on the current page. If we are on the first page we’ll only see the next arrow and if we are on the last page we’ll only see the previous arrow:
function updateNavigation( isLastPage ) { if( current === 0 ) { $navNext.show(); $navPrev.hide(); } else if( isLastPage ) { $navNext.hide(); $navPrev.show(); } else { $navNext.show(); $navPrev.show(); } }
When we open the TOC we want to hide the navigation arrows and we just show them again after closing the TOC.
We will animate the sidebar menu with a CSS transition. If there’s no support for transitions then a simple show/hide fallback will be used:
function toggleTOC() { var opened = $container.data( 'opened' ); opened ? closeTOC() : openTOC(); } function openTOC() { $navNext.hide(); $navPrev.hide(); $container.addClass( 'slideRight' ).data( 'opened', true ); } function closeTOC( callback ) { $navNext.show(); $navPrev.show(); $container.removeClass( 'slideRight' ).data( 'opened', false ); if( callback ) { if( supportTransitions ) { $container.on( transEndEventName, function() { $( this ).off( transEndEventName ); callback.call(); } ); } else { callback.call(); } } }
Phew! 🙂 That’s it! I hope you enjoyed this tutorial and find it useful!
thank for share awesome plugin i like it and i used it in my online app, i m looking for bottom to top and top bottom flip, if anybody know how to do this pls let me know.
its nice ..can you help in one thing i want to stop scroll on this page how i will stop it
Has anyone implemented links in the content pointing to TOC?
Has anyone solved the problem with associating the menu items with specific pages? I need menu items to flip to pages 2,10,15,etc..
Wow – I’ve never seen a web developer write such carefully crafted Javascript and CSS. Amazing tutorial, thanks!
Hi there,
Sorry for my bad English.
I found this a few weeks ago and using this one as a template for my blog.
I can mostly understand the mechanism each part how to work together.
Since I got a big index and I don’t know how to make the original TOC to show all the LI if it’s getting too long, I’ve modified the TOC part to a full screen style.
Everything works as long as they should be.
But I wanted to add some division or some descriptive words in the TOC part, e.g. “Archive for October below” and shows the entry name below.
The Original JS counts erery element in the UL as an entry, so if I do this, when visitors click a link, the JS will jump to one page offset to the true page.
So if I use the TOC as a multi-level index, it gets useless at all.
Here is my request.
(1) Is it possible if I put only specific entry link in the TOC but still correctly jump to that page without hacking. (I can do this by putting all entry link in the TOC and set display none to some of them, but I thing this is stupid.)
(2) Say if I got a BUTTON or a P or something else, how can I bind the JUMP function to it. I tried a lot, maybe I used wrong format, or maybe I cannot do this without using the TOC? I want to jump to any page within some special page just in the page itself and not opening the TOC. If I can only jump to a page by its number? Or can I jump to any page by its ID?
(Experienced user to HTML, CSS and JS(jQuery).)
Thank you so much. Any help would be appreciate.
Yuuki
Thank you for this GREAT plugin! it worked perfectly on IE,Chrome and mobile, but for some reason the TOC doesn’t work on firefox. it doesn’t jump to the page. By the way I have FF ver 15.0.1. any inputs/help will greatly appreciated. Thanks!
nevermind, I just need to update my firefox! i was way behind 🙂 my auto-update isn’t working. Thanks for this great plugin again!
Hi, I really love the tutorial. I only got a problem with the navigation. If i click the last item in then navigation level (after reloading), the content of the page is not scrollabel. But only for the last page. If I get there by clicking the arrows, it will scroll. Could you please help me with this. Thanx lissy
Like someone already mentioned, add this to line 518 in jquerybookblock.js.
this.previous = this.current; this._action(page > this.current ? 'next' : 'prev', page); this.current = page;
Thank you very much. That did help! Now I have two other problems. I tested it on mini iPad IOS7. Now the first page is cut off on the end after reload. Do you have a solution here, too? And the scrolling is really bad. Any Idea how to fix this? Thanx lissy
it is not working IE9,10. mainly, css are applying in IE
Does anybody know or have tried applying this to a Rails app and how to do it ? and is it compatible thanks looks amazing
Rails beginner
Hi, thanks for the nice tutorial! Any idea how to add extra pages?
You should be able to just add a new element with the bb-custom-side class. This should add additional pages.
Hey folks. So I am not sure if this is an issue or if it was intended by design. But I figured I would mention it. I was using the code from demo4 (full page book) as a template. However I noticed that whenever I add a new paragraph to a in the ‘bb-custom-side’ class, I tend to get too much space between the paragraphs. I did not notice this issue in looking at the blog post on the fullpage template, which is a different demo. http://tympanus.net/codrops/2012/12/11/fullscreen-pageflip-layout/ . So it look like there is a difference in the CSS between the two templates. It is a subtle thing, but I figured I would mention it. If you like I can mention it on the comments of the blog post as well. Do you guys know what the difference is in the CSS between these two templates?
Beautiful theme, indeed !
Just one question : how can I manage to display the “menu” button only, without the “next” one, if there is one file in the category?
Thanks for your answer – and sorry for my english.
Jean
I have fix the RTL Functionality.
Steps are .
1) Open file : jquery.bookblock
2) Line No 123
3) Change Value
this.current = 0;
// previous item’s index
4) Zero value representing page number from where you want to start. In case of Arabic, it should be the highest number of pages you have. So if have you 15 pages, use 15. If not work then use 14.
Next Step
5) Open: page.js
Goto Line No: 53:
Change bb.next(); to bb.prev();
Line No 58
Change bb.prev(); to bb.next();
Then go to line no : 68
Change next to prev
Then go to line no : 75 and change prev to next.
Then go to line no : 131
Change show to hide and in next line change hide to show.
Same changes for Line no 135 and 136.
Thats all.. This is what i just figured it out with my little knowledge. May be you can enhance it. Keep me posted.
Thanks
To make Menu links works properly you have to go to the line 87 in page.js and change code
from:
idx = $el.index(),
to:
idx = parseInt($el.children('a').attr('href').slice(5)),
Thanks Brother Yezyk
But its giving me error if i type the provided code.
Can u please check agan the syntax?
Thanks
try this, without ASCI code:
idx = parseInt($el.children('a').attr('href').slice(5)),
Hi this is amazing .. it’s horizontal page turn can we add 1 more arrow inside page content and create 1 more content . so when we click on arrow so it scroll to that content. For example one page website.
Is it possible to add the page-turn navigation buttons at the foot of each page? Technically, what would the function code be to call the page-turn functions by pressing an HTML button I could include at the foot of each page? Thank you.
To help clarify my last request. How can I trigger next() and prev() functions? Something like this: (to turn the page forward); and (to turn the page back). Hope this helps to clarify what I’m after. I would then add my buttons to the foot of each page.
Thx for the great Pageflip book! I wonder if you could help me to find out why https is not working. I scrolled through the js files in search for http (to point to https) but I didn’t succeed. Do you have a work-around with https? Thank you so much! Greeting, Inge