Adding Bootstrap Nav & Buttons & jQuery Tabs

Hi All,

I have been looking at changing the UI on Knack and making it more user friendly for my app, I have replaced the current horizontal menu with a Boostrap Nav menu which also sticks to the top when the page is scrolled and moves the loge from the header to the nav bar (See pics attached).

I have also made it put tables in views in to tabs rather than under each other using jQuery tabs.

I have not tested this lots yet and have seen some issues with the fixing of the navbar on scrolling (Bouncing), I have also only currently changed the navbar based on logged in or not but it can be improved with checking logged in user roles and showing/hiding individual menu items too.

There are probably many improvements but I just wanted to see if Knack could be changed to suit until the devs add these things to the core.

The code is below (JS and CSS) but you will need to use your own hosted logo

$(document).on('knack-page-render.any', function(event, page) {
  if (Knack.getUserAttributes(name) == "No user found") {
    if ($("#kn-app-menu2").length === 0) {
    $('#kn-app-menu').replaceWith('<div id="kn-app-menu2"><nav class="navbar navbar-default navbar-static-top" role="navigation"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header" style="display: none;"><button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="#home">Home</a></li><ul></li></ul></div><!-- navbar-collapse --></nav></div>');
    } else {
    $('#kn-app-menu2').replaceWith('<div id="kn-app-menu2"><nav class="navbar navbar-default navbar-static-top" role="navigation"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header" style="display: none;"><button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="#home">Home</a></li><ul></li></ul></div><!-- navbar-collapse --></nav></div>');
    }

} else {
if ($("#kn-app-menu2").length === 0) {
$(’#kn-app-menu’).replaceWith(’<div id=“kn-app-menu2”><nav class=“navbar navbar-default navbar-static-top” role=“navigation”><!-- Brand and toggle get grouped for better mobile display --><div class=“navbar-header” style=“display: none;”><button type=“button” class=“navbar-toggle” data-toggle=“collapse” data-target="#bs-example-navbar-collapse-1"><span class=“sr-only”>Toggle navigation</span><span class=“icon-bar”></span><span class=“icon-bar”></span><span class=“icon-bar”></span></button><a class=“navbar-brand” href="#"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class=“collapse navbar-collapse” id=“bs-example-navbar-collapse-1”><ul class=“nav navbar-nav”><li class=“active”><a href="#home">Home</a></li><li class=“dropdown”><a href="#" class=“dropdown-toggle” data-toggle=“dropdown” aria-expanded=“false”>Customers <b class=“caret”></b></a><ul class=“dropdown-menu”><li><a href="#customercompanies">Companies</a></li><li><a href="#customerdivisions">Divisions</a></li><li class=“divider”></li><li><a href="#customersites">Sites</a></li><li class=“divider”></li><li><a href="#">One more separated link</a></li></ul></li></ul><form class=“navbar-form navbar-left” role=“search”><div class=“form-group”><input type=“text” class=“form-control” placeholder=“Search”></div><button type=“submit” class=“btn btn-default”>Submit</button></form><ul class=“nav navbar-nav navbar-right”><li><a href="#">Link</a></li><li class=“dropdown”><a href="#" class=“dropdown-toggle” data-toggle=“dropdown”>Dropdown <b class=“caret”></b></a><ul class=“dropdown-menu”><li><a href="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li class=“divider”></li><li><a href="#">Separated link</a></li></ul></li></ul></div><!-- navbar-collapse --></nav></div>’);
} else {
$(’#kn-app-menu2’).replaceWith(’<div id=“kn-app-menu2”><nav class=“navbar navbar-default navbar-static-top” role=“navigation”><!-- Brand and toggle get grouped for better mobile display --><div class=“navbar-header” style=“display: none;”><button type=“button” class=“navbar-toggle” data-toggle=“collapse” data-target="#bs-example-navbar-collapse-1"><span class=“sr-only”>Toggle navigation</span><span class=“icon-bar”></span><span class=“icon-bar”></span><span class=“icon-bar”></span></button><a class=“navbar-brand” href="#"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class=“collapse navbar-collapse” id=“bs-example-navbar-collapse-1”><ul class=“nav navbar-nav”><li class=“active”><a href="#home">Home</a></li><li class=“dropdown”><a href="#" class=“dropdown-toggle” data-toggle=“dropdown” aria-expanded=“false”>Customers <b class=“caret”></b></a><ul class=“dropdown-menu”><li><a href="#customercompanies">Companies</a></li><li><a href="#customerdivisions">Divisions</a></li><li class=“divider”></li><li><a href="#customersites">Sites</a></li><li class=“divider”></li><li><a href="#">One more separated link</a></li></ul></li></ul><form class=“navbar-form navbar-left” role=“search”><div class=“form-group”><input type=“text” class=“form-control” placeholder=“Search”></div><button type=“submit” class=“btn btn-default”>Submit</button></form><ul class=“nav navbar-nav navbar-right”><li><a href="#">Link</a></li><li class=“dropdown”><a href="#" class=“dropdown-toggle” data-toggle=“dropdown”>Dropdown <b class=“caret”></b></a><ul class=“dropdown-menu”><li><a href="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li class=“divider”></li><li><a href="#">Separated link</a></li></ul></li></ul></div><!-- navbar-collapse --></nav></div>’);
}
}

var logo_height2 = $('#knack-logo').height();
var fixblock_pos2 = $('#kn-app-menu2').position().top + logo_height2;
var menu_height = $('#kn-app-menu2').height();
var noofscrolls = 0;

$(window).scroll(function(){

if ($(window).scrollTop() &gt; fixblock_pos2) {
  if (noofscrolls &lt; 1) {
  $('div.navbar-header').css('display', 'block');
  $('div.navbar-header &gt; a.navbar-brand').replaceWith('&lt;a class="navbar-brand" href="#"&gt;&lt;img src="https://s3.amazonaws.com/assets.knackhq.com/assets/YourAppID/logos/YourLogo.png" style="border: 0px; width: 80px; height: 50px"&gt;&lt;/a&gt;');
  $('#kn-app-menu2 &gt; nav').removeClass("navbar-static-top").addClass("navbar-fixed-top");
  $('.kn-view.kn-info').css({'position':'fixed', 'top': + menu_height + 'px'});
  noofscrolls = 1
  }
  
  } else {
  $('div.navbar-header').css('display', 'none');
  $('#kn-app-menu2 &gt; nav').removeClass("navbar-fixed-top").addClass("navbar-static-top");
  $('.kn-view.kn-info').css({'position': 'static', 'width': '100%'});
  noofscrolls = 0;           
  }

})

});

$(’<link rel=“stylesheet” href=“https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css” type=“text/css” media=“screen” /><link rel=“stylesheet” href=“https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css” type=“text/css” media=“screen” />’).insertBefore(’#knack-dist_1’);

var fwg_js_files = [‘https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js’, ‘https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js’, ‘https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js’];
LazyLoad.js(fwg_js_files, function () {

var tableName;
var tableRecordCount;

$(document).on(‘knack-records-render.any’, function(event, view, records) {
tableName = view.name;
tableRecordCount = records.length;
console.log (tableName + ‘,’ + tableRecordCount);
});

$(document).on(‘knack-scene-render.scene_34’, function(event, scene) {
$(“div.kn-table”).first().before(’<div id=“tabs”><ul>’);

$(“div.kn-table”).each(function (index){
$(this).parent().find(’#tabs > ul’).append(’<li><a href="#tabcontent-’ + index + ‘">’ + $(this).find(’.view-header > h2’).text() + ’ (’ + $(this).find(‘td.field_227’).text() + ‘)’ + ‘</a></li>’);
});

$(“div.kn-table”).each(function (index){
$(this).parent().find(’#tabs’).append(’<div id=“tabcontent-’ + index + '”>’);
});

$(“div.kn-table”).each(function (index){
$(this).appendTo(’#tabcontent-’ + index + ‘’);
});

jQuery( “#tabs” ).tabs();

});

$(document).on(‘knack-view-render.view_48’, function (event, view) {
// Add a bootstrap button group
$(’#’ + view.key).prepend(’<div class=“btn-group”><button id=“menu_button” class=“btn btn-default” contenteditable=“false”>Customers</button><button data-toggle=“dropdown” class=“btn btn-default dropdown-toggle” aria-expand=“false”><span class=“caret”></span></button><ul class=“dropdown-menu” contenteditable=“false”><li><a href="#">Companies</a></li><li><a href="#tabcontent-1">Jobs</a></li></ul></div>’);
});

});

div.kn-login {
margin-left: 50%;
transform: translate(-25%, 0%);
}

#kn-app-menu2 {
z-index: 9998;
}

.navbar {
margin-bottom: 0px !important;
}

.navbar-brand {
padding: 1px 15px !important;
}

#tabs {
margin: 3px;
border: none !important;
}

.ui-tabs-panel {
border: 1px solid #aaa !important;
}

.ui-widget-header {
border: none !important;
background: none !important;
}

ul.ui-tabs-nav li a {
background: #074376 !important;
color: #fff !important;
}

ul.ui-tabs-nav li.ui-state-active a {
background: #fff !important;
color: #000 !important;
outline: none !important;
}

The CSS is for the attached screenshots so remove/change as required.

Enjoy :slight_smile:

Thanks

Dan

Hi Dan

This is an old post but I like it. Has the code evolved any further? Also would be great if you could quickly post some screen grabs. They don't appear in your first post.

Thanks in advance :)

Phil

Hi,

It still says post pending approval my end so assume that is why. Hopefully the team will do that soon then.

Thanks

Dan

Hey Dan, that last update responds with 'don't have permission to view' which is weird. I've logged this with the Knack team too.

Can you access that page?

Hi,

Updated and working code for adding jQuery tabs here: https://support.knack.com/hc/en-us/community/posts/115001242728-Add-Real-jQuery-Tabs-for-each-table

Thanks

Dan

Hi,

Updated the request here: http://helpdesk.knackhq.com/support/discussions/topics/5000058111

Thanks

Dan

Hi,

I have been waiting on the dev team to add a hook to the AJAX call so i could finalise the tabs and other areas but no movement :(

I would like them to add a "Tab" field, with the options "Start Tab Group", "Start Tab" and "End Tab Group" so then you can just drag 'n' drop these field around your other fields to create tabs as and where you like.

I will check if I created a Feature Request or not and if not add one now.

Thanks

Dan

Hi Daniel,

Just wondering how things are going with the new UI, have you had to update it based on the latest Knack update?

Thanks,

Mark

This works like a charm when viewing the app from knack, but the menu stops working after i embed it in my website. Perhaps some sort of conflict with the existing css.

Hi All,

Still working out a few things as page load and Knacks AJAX load behave slightly different so once I have sorted this I will split this in to separate posts for navbar, button and split buttons and tabs.

Thanks

Dan

Hi Daniel,

Thank you for sharing this.

Do you mind helping me - I am not technical at all.

Which parts of your code should I use, if I only need the tables in views into tabs rather than under each other?


Thanx,


Chrislee

Oh wow! your app looks amazing!! We do not understand anything you just posted, but we'd loooove for our tabs to look like this! Could you help us? We've been trying desperately to find someone to change the dated look of our app with those tabs, but we never get a response! How can we get our tabs to look like yours? / See shot below to see what ours look like now...

Hi, Few more updates/improvements:

1) CSS to correct the first tab being shifted right slightly:

.ui-tabs .ui-tabs-nav {
  	padding: 0 !important;
}

2) Updated the tabs code to include the record count in the tab along with the text (Taken from the table name in the UI) and using an icon (Taken from the first column if an icon is present) and applied it to all tables (You can ad a scene/view condition before to control it):

var tableCountLoops = 0;  
$(document).on('knack-records-render.any', function(event, view, records) {
var iconClassName = $('#' + view.key + '.kn-table > .kn-table-table > tbody > tr:first > td:first').find('i').attr('class');
var tableCount = $('.kn-table').length;
tableCountLoops++;  
if ($("#tabs").length === 0) {
$('.kn-table').first().before('<div id="tabs"><ul>'); 
};  
$('#' + view.key + '.kn-table').parent().find('#tabs > ul').append('<li><a href="#tabcontent-' + view._id + '"><i class="'+ iconClassName +'" style="margin-right: 0.4em"></i>' + view.name + ' (' + records.length + ')' + '</a></li>'); 
$('#' + view.key + '.kn-table').parent().find('#tabs').append('<div id="tabcontent-' + view._id + '">'); 
$('#' + view.key + '.kn-table').appendTo('#tabcontent-' + view._id + ''); 

if (tableCountLoops === tableCount) {
jQuery( “#tabs” ).tabs();
tableCountLoops = 0;
}
});

3) Updated the sticky menu to stop it jumping:

var menu = ('#kn-app-menu2');  
var menu_height = (’#kn-app-menu2’).outerHeight( true );
var infobar_height = $(’.kn-view.kn-info’).outerHeight( true );
var menu_infobar_height = menu_height + infobar_height;

var origOffsetY = menu.offset().top;

if (("#menu_filler").length === 0) { (’.kn-view.kn-info’).before(’<div id=“menu_filler”></div>’);
}

function scroll() {
    if ($(window).scrollTop() &gt;= origOffsetY) {
  		$('div.navbar-header').css('display', 'block');
      	$('div.navbar-header &gt; a.navbar-brand').replaceWith('&lt;a class="navbar-brand" href="#"&gt;&lt;img src="https://s3.amazonaws.com/assets.knackhq.com/assets/5519882103a92a0b715baec3/logos/200x83-Transparent---Copy.png" style="border: 0px; width: 80px; height: 50px"&gt;&lt;/a&gt;');
  		$('#kn-app-menu2 &gt; nav').removeClass("navbar-static-top").addClass("navbar-fixed-top");
  		$('.kn-view.kn-info').css({'position':'fixed', 'top': + menu_height + 'px'});
      	$('#menu_filler').css({display: 'block', height: + menu_infobar_height + 'px'});
    } else {
  		$('div.navbar-header').css('display', 'none');
      	$('#kn-app-menu2 &gt; nav').removeClass("navbar-fixed-top").addClass("navbar-static-top");
  		$('.kn-view.kn-info').css({'position': 'static', 'width': '100%'});
      	$('#menu_filler').css({display: 'none'});
    }
}
document.onscroll = scroll;</pre><p></p><p> Just have one issue where when a Modal Dialog is opened with the menu stuck at the top it changes the menu class back to static so it goes above the viewport until scrolled again.</p><p>And some CSS updates:</p><p>1) To make the titles of a group on a page span the width of the group not just the first column:</p><p> </p><pre rel="highlighter">td.title {
display: table-cell !important;

}

2) For the new menu code:

#menu_filler {
display: none;
width: 100%;
}

3) Updated z-index for my layout (I have bootstrap menus, buttons, split buttons, e.t.c.) so change as required:

ul.nav.navbar-nav > li.dropdown > ul.dropdown-menu {
min-width: 100px !important;
z-index: 999 !important;
}

.navbar-static-top {
z-index: 999 !important;
}

.navbar-fixed-top {
z-index: 999 !important;
}

.kn-view.kn-info {
z-index: 998 !important;
}

div.btn-group > ul.dropdown-menu {
min-width: 100px !important;
z-index: 997 !important;
}

Some example screens attached.

Thanks

Dan

The last comment above replaces Lines 23 to 47 in the original

Hi, Quick update on the fixed navbar that appears to stop the bouncing/flickering issue with certain height screens:

var menu_height = $('#kn-app-menu2').height();
var menu = $('#kn-app-menu2');
var origOffsetY = menu.offset().top;
function scroll() {
    if ($(window).scrollTop() &gt;= origOffsetY) {
        $('div.navbar-header').css('display', 'block');
  		$('div.navbar-header &gt; a.navbar-brand').replaceWith('&lt;a class="navbar-brand" href="#"&gt;&lt;img src="https://s3.amazonaws.com/assets.knackhq.com/assets/YOUR APP ID/logos/name.ext" style="border: 0px; width: 80px; height: 50px"&gt;&lt;/a&gt;');
  		$('#kn-app-menu2 &gt; nav').removeClass("navbar-static-top").addClass("navbar-fixed-top");
  		$('.kn-view.kn-info').css({'position':'fixed', 'top': + menu_height + 'px'});
    } else {
        $('div.navbar-header').css('display', 'none');
  		$('#kn-app-menu2 &gt; nav').removeClass("navbar-fixed-top").addClass("navbar-static-top");
  		$('.kn-view.kn-info').css({'position': 'static', 'width': '100%'});
    }


}

document.onscroll = scroll;</pre><p></p><p> </p><p>Thanks</p><p>Dan</p>

Awesome Daniel - thanks for sharing.

This really supports the wisdom of the Knack team to include a JavaScript & CSS editor for us.

Now to read your work in detail Daniel...

Hi,

The record count part was/will be to show the number of records in the table on the tab itself (Where I have the empty () today). Later I would/will add icons to the tabs too.

Thanks

Dan

Hi,

And of course change the view/scene/e.t.c. names to suit yours

Thanks

Dan