Das 3 Purpose Grid soll weniger durch viele Gui-Elemente glänzen, von denen man in der Praxis meist nur einen Bruchteil benötigt. Vielmehr soll es vor allem schlank und vielseitig sein und den Entwickler-Workflow unterstützen.
Auf zwei GUI-Elemente kann man aber nicht verzichten, wenn man schnell ein funktionsfähiges System als Ausgangspunkt für ein größeres Projekt aufsetzen möchte, auf:
- Navigationsmenus und
- Paginierung.
Unser System (Demo) wird deshalb im Folgenden um diese Ressourcen erweitert.
Beginnen wir, weil es der einfachere Teil ist, mit der Paginierung, um dann im zweiten Teil dieses Artikels ein einfaches responsives Collapsible-Menu vorzustellen.
Ein aufwändigeres Menu-System, das mehrere hierarchische Menus auf einer Seite integrieren kann, habe ich ebenfalls entwickelt, wird aber im Rahmen unserer Projekte hier im Blog nicht benötigt.
Paginierung
Eine Paginierung ist eine Seitennumerierung, mit deren Hilfe ein User einen Wechsel eines sequentiell organisierten Inhalts herbeiführen kann, ähnlich dem Umblättern in einem Buch. Entweder wird der ganze Seiteninhalt ausgetauscht oder der User klickt sich damit durch Inhalte in Info-Boxen, Bilder- und sonstige Mediengallerien.
Ich habe für unser Grid das schöne jQuery-Plugin simplePagination gefunden, das alle Anforderungen für ein allgemeines CSS-Framework, wie es unser Grid-System darstellt erfüllt (vgl. Abb. 1):
- simplePagination lässt mittels CSS-Regeln responsiv einstellen,
- es lässt sich mittels Javascript-Konfiguration mehrmals für verschiedene Inhalte auf einer Seite platzieren, z.B. als Seitenpaginierung, Bildergallerie und für das Jahresarchiv z.B. auf einer aside-Spalte,
- man kann sowohl einen Inhaltsaustausch per Ajax herbeiführen mittels Location-Hash (z.B. http://meine.seite.de/index.html#page=2-xy), als auch einen Wechsel der kompletten Seite herbeiführen mittels Url-Query (z.B. http://meine.seite.de/index.html?page=10).
Abb. 1: Paginierung mit dem jQuery-Plugin simplePagination
Das jQuery-Plugin simplePagination bekommt man hier.
In unserer Datei-Struktur speichert man die kompletten Sourcen am besten in einem libs-Ordner unterhalb des js-Ordners (vgl. Abb. 2):
Abb. 2: Ablage der jQuery-Plugins simplePagination-Sourcen in der Dateistruktur
Wir benötigen ein eigenes CSS-File, um simplePagination an unser System anzupassen (vgl. auch Abb. 2, Datei resp-pagi-whr-nrw-var-1-vers-0.5.0.css).
/* (c) Dr. Wolfgang Hauertmann, Hilden 2015, Lizenz: siehe lizenz.txt */ /* * Integration of the jQuery-Plugin simplePagination */ #node-pagination, #node-pagination1{ text-align: center; /* ie */ width:100%; } #node-pagination ul, #node-pagination1 ul{ text-align: center; /* ie */ display:block; /* center horizontal */ position: relative; float: left; left: 50%; margin: 0 auto; padding: 0; } #node-pagination li, #node-pagination1 li{ /* center horizontal */ position: relative; float: left; right: 50%; } /* One-Column pagination */ #node-pagination1 ul li { display: none; } #node-pagination1 ul li:first-of-type, #node-pagination1 ul li:last-of-type, #node-pagination1 ul li.active.active, #node-pagination1 ul li:nth-child(2), #node-pagination1 ul li:nth-last-child(2) { display:inline; } /* Responsive */ /** * Old feature phones or similar */ @media (max-width: 366px) { #node-pagination ul li { display: none; } #node-pagination ul li:first-of-type, #node-pagination ul li:last-of-type, #node-pagination ul li.active.active, #node-pagination ul li:nth-child(2), #node-pagination ul li:nth-last-child(2) { display:inline; } } /* e.g. Samsung 85008i max. width: 240 px */ @media (max-width: 300px) { #node-pagination ul li, #node-pagination ul li:nth-child(2), #node-pagination ul li:nth-last-child(2){ display: none; } #node-pagination ul li:first-of-type, #node-pagination ul li:last-of-type, #node-pagination ul li.active { display:inline !important; } }
3-Purpose-Grid - File: assets/css/resp-pagi-whr-nrw-var-1-vers-0.5.0.css
Diese Datei verwendet Identifier für zwei Pagination-Elemente auf der Seite (vgl. Abb. 1): node-pagination und node-pagination1. Die 1 steht für one column. Diese Elemente werden in der index...html-Seite dort als div-Tags eingebaut, dort wo man sie benötigt:
.... <!-- Pagination --> <link href="./assets/js/libs/simplePagination/simplePagination.css" rel="stylesheet" type="text/css"/> <link href="./assets/css/resp-pagi-whr-nrw-var-1-vers-0.5.0.css" rel="stylesheet" type="text/css" /> ... </head> <body> .... <!-- Pagination --> <div id="node-pagination"></div> .... <!-- Pagination --> <div id="node-pagination1"></div> .... ... <!-- Pagination --> <script type="text/javascript" src="./assets/js/libs/simplePagination/jquery.simplePagination.js"></script> <script type="text/javascript" src="./assets/js/mod/resp-pagi-whr-nrw-var-1-vers-0.5.0.js"></script> ... </body> </html>
3-Purpose-Grid - File: index-855.3.resp.head2.foot2.navi.pagi.html
In der hier nicht vollständig wiedergegebenen index...html-Datei, die man aus einem der vorherigen Teile dieser Reihe übernehmen und um die entsprechenden Code-Snippets ergänzen kann, erkennt man unten den Script-Link auf das jQuery-Plugin simplePagination und ein eigenes Skript, mit dem unsere beiden Paginierungen initialisiert werden. Dieses Skript sieht so aus:
/* (c) Dr. Wolfgang Hauertmann, Hilden 2015, Lizenz: siehe lizenz.txt */ /* * Integration of the jQuery-Plugin simplePagination */ // http://flaviusmatis.github.io/simplePagination.js/#page-11 // Parameters see here: http://flaviusmatis.github.io/simplePagination.js/ // Attention: it works fine with hrefTextPrefix: '#...' but not with '?...'! $(document).ready(function() { $("#node-pagination").pagination({ items: 100, itemsOnPage: 10, cssStyle: 'light-theme', displayedPages:3, edges:1, hrefTextPrefix: '#page=', hrefTextSuffix: '-xy', onPageClick: function(pageNumber, event) { debug.hint2(2000, 'hint2: #page=' + pageNumber + '-xy', 'notice'); // ... do something with pageNumber ... }, onInit: function() { // debug.hint2(2000, 'hint2: #node-pagination initalized', 'success'); } }); /* One column pagination */ // Attention: A reload of the page will be initiated. page = util.getQueryParameterByName('page'); $("#node-pagination1").pagination({ items: 100, itemsOnPage: 10, cssStyle: 'light-theme', displayedPages:3, edges:1, currentPage: page, // !!!! hrefTextPrefix : "?page=", onPageClick: function(pageNumber, event) { window.location.href="?page="+pageNumber; }, onInit: function() { // debug.hint2(2000, 'hint2: #node-pagination1 initalized', 'success'); } }); });
3-Purpose-Grid - File: assets/js/mod/resp-pagi-whr-nrw-var-1-vers-0.5.0.js
Anhand der beiden Paginierungselemente werden zwei verschiedene Arten der Integration demonstriert.
#node-pagination stellte eine Paginierung dar, die per Ajax-Call einen neuen Inhalt zur Verfügung stellt, der dann in onPageClick implementiert wird. Typischerweise wird hier ein Location-Hash verwendet (...#page=2-xy), der nicht zu einem Neuladen der kompletten Seite führt.
#node-pagination1 steht exemplarisch für eine Paginierung, die den kompletten Seiteninhalt ändert (...?page=9).
Damit steht von Seiten des Frameworks aus alles Notwendige zur Verfügung, um schnell eine flexible und optisch ansprechende Paginierung zu implementieren zum Beispiel für CMS-Inhalte, Bilder-, Mediengallerien und Infoboxen.
Navigationsmenu
Für unser responsives Navigationsmenu fehlt eigentlich nur noch ein Javascript, welches uns ein Collapsible-Menu unterhalb des Menu-Buttons erzeugt, der unterhalb der Breite einer definierten Umbruch-Stelle erscheint, die wir mittels Media-Queries festgelegt haben (vgl. Abb. 3).
Abb. 3: 3 Purpose Grid: Menu-Button bei Unterschreiten einer definierten Breite
Die meisten Vorarbeiten dafür - Html und CSS-Styles - hatten wir schon in Teil 3 implementiert. Hier das noch fehlende Javascript:
/* (c) Dr. Wolfgang Hauertmann, Hilden 2015, Lizenz: siehe lizenz.txt */ /* * Collapsible Menu-Button (Version 1) */ $(document).ready(function() { // Responsive Menu var downFlag = false; var upFlag = false; var navDown = false; function collapseDown() { if (navDown) return; if (downFlag) return; downFlag = true; $(".nav.collapsible").appendTo("nav.collapsed"); // Transfer invisible nav-menu to target area var nav_h = $(".nav.collapsible").height()+1; // Store 'height' as parameter for animation ( +1: add some space to the bottom. Not needed but looks nice. ) $(".nav.collapsible").css({ height : 0 }); // Set 'height' to 0 as starting point for animation $(".nav.collapsible").css('display', 'block'); // Make .nav.collapsible visible (initially .nav.collapsible has style: 'display:none;', see. respNav.css) $(".nav.collapsible").stop().animate({"height":nav_h}, 300, "linear", function() { $("nav.nav-top-right .nav.collapsible").remove(); downFlag=false; navDown = true; }); } function collapseUp() { if (!navDown) return; if (upFlag) return; upFlag = true; $(".nav.collapsible").stop().animate({height:0}, 300, "linear", function() { $(".nav.collapsible").css('display', 'none'); $(this).removeAttr("style").insertBefore("nav.nav-top-right #menu"); $("nav.collapsed .nav.collapsible").remove(); upFlag = false; navDown = false; }); } // You can do that here but it flickers on loading page. It is better to declare this into the css-styles // $(".nav").css('display', 'none'); // see: @media screen and (max-width: 768px) ... // $(".nav").first().after('<div id="menu">☰</div>'); // nav-button for small mobile devices $("#menu").click(function(){ if ($("nav.nav-top-right ul.nav").length > 0) { collapseDown(); } else { collapseUp(); } }); $(window).resize(function(){ collapseUp(); }); });
3-Purpose-Grid - File: assets/js/mod/resp-navi-whr-nrw-var-1-vers-0.5.0.js
Dieses Skript ist ausgelegt dafür, die Einträge des Top-Menus zu übertragen in das div-Tag mit der Klasse collapsed, wenn man den Navigations-Button anklickt:
.... <main> <nav class='collapsed'></nav> .... <!-- Top-right-menu --> <nav class='nav-top-right'> <ul id="nav-top-right" class="nav collapsible" > <li><a href="#">Home</a></li> <li><a href="http://code-kiste.hauertmann.com/" target="_blank">Blog</a></li> <li><a href="http://hauertmann.com/content/leistungen-und-kompetenzen" target="_blank">Support</a></li> </ul> <button id="menu"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </nav>
3-Purpose-Grid- File: index....html
Im Falle dieses einfachen Menus darf man nur ein Listen-Element mit der Klasse collapsible kennzeichnen auf einer Seite (vgl. Abb. 4), sonst funktioniert es nicht richtig. Auch sind hierarchische Menus nicht möglich.
Abb. 4: 3 Purpose Grid: Collapsed Menu
Es existiert eine erweiterte Variante, in der eine beliebige Anzahl hierarchischer Menus (bis zu 3 Ebenen und mit entsprechender CSS-Modifikation auch mehr) auf einer Seite möglich sind, die aber für Projekte in diesem Blog nicht benötigt wird.
Vielleicht wundert sich jemand, dass sowohl das Paginierungsskript als auch das Navigationsskript (noch) nicht auf dem Modul-Pattern basieren und trotzdem im Ordner mod gespeichert sind. Nun, diese kleinen Skriptchen sind so simpel, dass ich mir das Modul-Pattern noch gespart habe. In einer Ausbaustufe, z.B. für das Premium-Navigationsskript lohnt sich der Aufwand dann. Deshalb habe ich diese beiden Skripte aus Inhalts-systematischen Gründen ebenfalls unter mod abgelegt. In gewissem Sinne sind es ja auch Module, nur halt ohne Modul-Pattern. Das Pattern ist ja schließlich nicht Pflicht.
Fazit und Ausblick
Wir haben jetzt unser 3 Purpose Grid um zwei leistungsfähige und unverzichtbare GUI-Elemente erweitert, um responsive Paginierung und Hauptmenu-Navigation.
Damit erfüllt unser System schon die Mindestanforderungen für ein kleines Framework. Allerdings werden wir im nächsten Teil 5 noch ein paar Schönheits-Features spendieren: Styles für Menus, Medien, Info-Boxen und Formulare.
Dabei werden wir es, was die GUI-Elemente betrifft, belassen und alles weitere, die Gestaltung von Buttons, Reitern und sontigen Elementen dem Designer überlassen.
Damit hätten wir den ersten Zweck unseres Frameworks, ein Grid-System für Portal-Projekte, erfüllt.
Weitere Teile befassen sich mit dem Einsatz als GUI-Konsole und als Ausgangslayout für Hybride Apps.