It's not the same without you
Join the community to find out what other Atlassian users are discussing, debating and creating.
In order to make navigating within a page faster and easier, I would like to have the table of content always visible, even if scrolling down.
That’s why my question is: Is it possible to make the table of content move with you as you scroll down the page?
If that is possible, I would always have an overview over the content on the page.
Thanks a lot for your help!
You could do it with some css and javascript. In your Confluence admin create a css class like the below.
.fixedPosition { position: fixed; }
In the custom html section put this in the after body section.
<script type="text/javascript"> AJS.toInit(function(){ AJS.$('.fixedPosition').each(function(){ var clone = AJS.$(this).clone().css('visibility','hidden').removeClass('fixedPosition'); AJS.$(this).after(clone); }); }); </script>
Then on your table of contents macro put the class "fixedPosition" at the bottom of the properties.
What this will do is clone your table of contents and hide the clone to reserve the original space of the table of contents because when you do a position:fixed the element is pulled out of flow and the content below it will move up.
Is this what you are looking to do?
Hi Davin,
sorry for not answering earlier. I haven't found any time to try it. FInally I did try it today and your solution definetely tends into the right direction. Thanks a lot for your help!
However the TOC now is cloned over the regular content (as you have mentioned in your post). The problem is that the TOC now is not readable anymore, because it overlays the regular text. A (white) background would also not solve the problem, because a TOC may be so large that it covers a whole page. So I guess a perfect solution would be:
A kind of frame which is at the top of the page and contains the TOC only. I will name it "TOC-frame" from now on.
When scrolling down the page the TOC-frame is minimized to a small bar which maximizes on mouseover (or even hidden and only showing up on mouseover on the top of the page). By this the user can navigate using the TOC without seeing it all the time.
Does anyone think that is possible and how do I have to implemet it?
Thanks a lot!
I expected soemthing like that... Anyhow thanks again for your input, the other solution works ok. Finally I combined 3 things:
- a standard confluence range (2/1 split). On the left a real TOC and on the right:
- "highlight" macro for a solid background. Here comes the "fixedPosition" CSS
- "expand" macro for the ability to have it as a one row TOC-"indicator"
When starting the page the user sees the full TOC on the left and only the minimized small 1-row-TOC on the right (which is not of interest at the moment). When scrolling down, the real TOC disappears but the one-row-expand macro moves with the page. When expanding the macro the TOC shows up.
This solution is still far from being perfect (not docked on top, covering other stuff, not disappearing after click,...), but ok. If anybody has ideas for any better solutions -> please let me know!
I just had the same request.
Because I don't like to modify default confluence (or some might not have the rights), I added a grease-/tampermonkey script which adds a collapsible TOC at the right side of the page.
It's not the prettiest thing but it works. Here's the script for it:
// ==UserScript== // @name Confluence floating toc // @namespace http://tampermonkey.net/ // @version 0.1 // @description Adds a floating toc into Confluence // @author RBL // @match <your confluence url> // @grant none // ==/UserScript== (function() { 'use strict'; $('[data-macro-name="toc"]').each(function(){ var clone = $(this).clone(); var list = clone.children(":first"); list.attr("id", "floatingToc"); list.css("padding", "0"); list.css("list-style-type", "none"); clone.css("padding", "5px"); clone.css("position", "fixed"); clone.css("background", "white"); clone.css("border", "1px solid grey"); clone.css("right", "0"); clone.css("top", "100px"); clone.css("z-index", "9999"); clone.prepend("<div style='float:right;'><a id='showTocLink' href='javascript:$(\"#floatingToc\").show();$(\"#showTocLink\").hide();$(\"#hideTocLink\").show();'>Show</a>&nbsp;<a id='hideTocLink' href='javascript:$(\"#floatingToc\").hide();$(\"#showTocLink\").show();$(\"#hideTocLink\").hide();'>Hide</a></div>"); $(this).after(clone); $("#showTocLink").hide(); }); })();
Maybe this helps someone
I already have an update so that it also works with very long Tocs (scrollable).
Here's the update:
// ==UserScript== // @name Confluence floating toc // @namespace http://tampermonkey.net/ // @version 0.2 // @description Adds a floating toc into Confluence // @author RBL // @match <your url here> // @grant none // ==/UserScript== (function() { 'use strict'; $('[data-macro-name="toc"]').each(function(){ var clone = $(this).clone(); var list = clone.children(":first"); list.attr("id", "floatingToc"); list.css("padding", "0"); list.css("list-style-type", "none"); clone.attr("id", "floatingTocContainer"); clone.css("padding", "5px"); clone.css("position", "fixed"); clone.css("background", "white"); clone.css("border", "1px solid grey"); clone.css("right", "0"); clone.css("top", "80px"); clone.css("max-height", "calc(100% - 100px)"); clone.css("overflow-y", "auto"); clone.css("z-index", "9999"); clone.prepend("<div style='float:right;'><a id='showTocLink' href='javascript:$(\"#floatingToc\").show();$(\"#showTocLink\").hide();$(\"#hideTocLink\").show();'>Show</a><a id='hideTocLink' href='javascript:$(\"#floatingToc\").hide();$(\"#showTocLink\").show();$(\"#hideTocLink\").hide();'>Hide</a></div>"); $(this).after(clone); $("#showTocLink").hide(); }); })();
Sorry for the spam, I have one more updated version: Smaller font and some configuration values at the top:
// ==UserScript== // @name Confluence floating toc // @namespace http://tampermonkey.net/ // @version 0.3 // @description Adds a floating toc into Confluence // @author RBL // @match <your confluence url> // @grant none // ==/UserScript== (function() { 'use strict'; var fontSize = '10px'; var distanceTop = '80px'; var distanceBottom = 'calc(100% - 100px)'; var hideOnStart = false; $('[data-macro-name="toc"]').each(function(){ var clone = $(this).clone(); var list = clone.children(":first"); list.attr("id", "floatingToc"); list.css("padding", "0"); list.css("list-style-type", "none"); clone.attr("id", "floatingTocContainer"); clone.css("padding", "5px"); clone.css("position", "fixed"); clone.css("background", "white"); clone.css("border", "1px solid grey"); clone.css("right", "0"); clone.css("top", distanceTop); clone.css("max-height", distanceBottom); clone.css("overflow-y", "auto"); clone.css("z-index", "9999"); clone.css("font-size", fontSize); clone.prepend("<div style='float:right;'><a id='showTocLink' href='javascript:$(\"#floatingToc\").show();$(\"#showTocLink\").hide();$(\"#hideTocLink\").show();'>Show</a><a id='hideTocLink' href='javascript:$(\"#floatingToc\").hide();$(\"#showTocLink\").show();$(\"#hideTocLink\").hide();'>Hide</a></div>"); $(this).after(clone); if (hideOnStart) { $("#floatingToc").hide(); $("#showTocLink").show(); $("#hideTocLink").hide(); } else { $("#floatingToc").show(); $("#showTocLink").hide(); $("#hideTocLink").show(); } }); })();
And another update:
The ToC is now also visible on pages which do not have their own toc! I generelly build my own toc in all cases. Also there is some more configuration (check the variables at the top).
// ==UserScript== // @name Confluence floating toc // @namespace http://tampermonkey.net/ // @version 0.4 // @description Adds a floating toc into Confluence // @author RBL // @match <your confluence url> // @grant none // ==/UserScript== // Setup variables var fontSize = '10px'; // General size of the links var distanceTop = '80px'; // Distance from the top var distanceBottom = 'calc(100% - 100px)'; // Distance from the bottom var initiallyCollapsed = false; // Flag if it should be collapsed when loaded var showWithoutToc = true; // Flag if it should be created when no toc was added to the page var hideEmpty = true; // Flag if empty levels should be hidden // Entry "class" function Entry(id, text, level, parent) { this.id = id; this.text = text; this.level = level; this.parent = parent; this.subElements = []; } Entry.prototype.addChild = function(child) { this.subElements.push(child); }; // "Main" (function() { 'use strict'; if (!showWithoutToc) { // Check if the toc exists if (!$('[data-macro-name="toc"]').length) { // It doesn't so don't do anything return; } } // Build the list with headers var rootElement = new Entry('root', '', 0, undefined); var lastElement = rootElement; $('#main-content').find($("h1, h2, h3, h4, h5, h6")).each(function() { // Parse the object information var thisLevel = parseInt($(this).prop("nodeName").substring(1)); var thisId = $(this).prop("id"); var thisText = $(this).text(); // Check for level changes with the last element var levelDiff = thisLevel - lastElement.level; if (levelDiff > 0) { // Higher level for (var i=1; i<levelDiff; i++) { // Add intermediate elements if some levels were skipped var intermediateElement = new Entry('intermediate', '', lastElement.level+i, lastElement); lastElement.addChild(intermediateElement); lastElement = intermediateElement; } var thisElement = new Entry(thisId, thisText, thisLevel, lastElement); lastElement.addChild(thisElement); lastElement = thisElement; } else if (levelDiff <= 0) { // Same or lower level // Loop to go down as needed for (var i=0; i<Math.abs(levelDiff); i++) { lastElement = lastElement.parent; } var thisElement = new Entry(thisId, thisText, thisLevel, lastElement.parent); lastElement.parent.addChild(thisElement); lastElement = thisElement; } }); // Log //rootElement.subElements.forEach(function(entry) { console.log(entry); }); var newElement = $('<div></div>'); var currentList = $('<ul></ul>'); newElement.append(currentList); var currentLevel= 1; rootElement.subElements.forEach(function(entry) { createEntry(currentList, entry); }); $('#main-content').append(newElement); // Add styles and ids newElement.attr("id", "floatingTocContainer"); newElement.css("padding", "5px"); newElement.css("position", "fixed"); newElement.css("background", "white"); newElement.css("border", "1px solid grey"); newElement.css("right", "0"); newElement.css("top", distanceTop); newElement.css("max-height", distanceBottom); newElement.css("overflow-y", "auto"); newElement.css("z-index", "9999"); newElement.css("font-size", fontSize); newElement.prepend("<div style='float:right;'><a id='showTocLink' href='javascript:$(\"#floatingToc\").show();$(\"#showTocLink\").hide();$(\"#hideTocLink\").show();'>Show</a><a id='hideTocLink' href='javascript:$(\"#floatingToc\").hide();$(\"#showTocLink\").show();$(\"#hideTocLink\").hide();'>Hide</a></div>"); currentList.attr("id", "floatingToc"); currentList.css("padding", "0"); currentList.css("list-style-type", "none"); currentList.css("font-size", fontSize); currentList.css("line-height", 1); // Initialize $("#floatingToc").toggle(!initiallyCollapsed); $("#showTocLink").toggle(initiallyCollapsed); $("#hideTocLink").toggle(!initiallyCollapsed); })(); function createEntry(parentList, entry) { var listItem = $('<li/>', {html: '<a href="#' + entry.id + '">' + entry.text + '</a>'}); // Hide the list icon for helper levels if (entry.id == 'intermediate') { listItem.css("list-style-type", "none"); } // Add the subelements if (entry.subElements.length > 0) { var innerList = $('<ul></ul>'); innerList.css("padding-left", "20px"); var hasRealChild = false; entry.subElements.forEach(function(innerEntry) { createEntry(innerList, innerEntry); if (innerEntry.id != 'intermediate') { hasRealChild = true; } }); // Check if the subelements contain at least one real child if (hideEmpty && !hasRealChild) { // It doesn't, so hide this level completely innerList.css("padding", "0px"); } listItem.append(innerList); } parentList.append(listItem); }
You actually do not add it to confluence. It is "injected" into the website via an addon to your browser (Tampermonkey for Chrome, Greasemonkey for Firefox). With this addon you can add JavaScript code which should be injected on pages. You need to adjust the @match
in the script above to match your confluence base url.
Hope this helped.
Here's a new version of it by the way. This one should work better when skipping some headings (eg. using only h1 and h3):
// ==UserScript== // @name Confluence floating toc // @namespace http://tampermonkey.net/ // @version 0.5 // @description Adds a floating toc into Confluence // @author RBL // @match <your confluence url> // @grant none // ==/UserScript== // Setup variables var fontSize = '10px'; // General size of the links var distanceTop = '80px'; // Distance from the top var distanceBottom = 'calc(100% - 100px)'; // Distance from the bottom var initiallyCollapsed = false; // Flag if it should be collapsed when loaded var showWithoutToc = true; // Flag if it should be created when no toc was added to the page var hideEmpty = true; // Flag if empty levels should be hidden // Entry "class" function Entry(id, text, level, parent) { this.id = id; this.text = text; this.level = level; this.parent = parent; this.subElements = []; } Entry.prototype.addChild = function(child) { this.subElements.push(child); }; // "Main" (function() { 'use strict'; if (!showWithoutToc) { // Check if the toc exists if (!$('[data-macro-name="toc"]').length) { // It doesn't so don't do anything return; } } // Build the list with headers var rootElement = new Entry('root', '', 0, undefined); var lastElement = rootElement; $('#main-content').find($('h1, h2, h3, h4, h5, h6')).each(function() { // Parse the object information var thisLevel = parseInt($(this).prop('nodeName').substring(1)); var thisId = $(this).prop('id'); var thisText = $(this).text(); // Check for level changes with the last element var levelDiff = thisLevel - lastElement.level; if (levelDiff > 0) { // Higher level for (var i = 1; i < levelDiff; i++) { // Add intermediate elements if some levels were skipped var intermediateElement = new Entry('intermediate', '', lastElement.level + i, lastElement); lastElement.addChild(intermediateElement); lastElement = intermediateElement; } var thisElement = new Entry(thisId, thisText, thisLevel, lastElement); lastElement.addChild(thisElement); lastElement = thisElement; } else if (levelDiff <= 0) { // Same or lower level // Loop to go down as needed for (var i = 0; i < Math.abs(levelDiff); i++) { lastElement = lastElement.parent; } var thisElement = new Entry(thisId, thisText, thisLevel, lastElement.parent); lastElement.parent.addChild(thisElement); lastElement = thisElement; } }); // Log //rootElement.subElements.forEach(function(entry) { console.log(entry); }); var newElement = $('<div></div>'); var currentList = $('<ul></ul>'); newElement.append(currentList); var currentLevel= 1; createEntry(rootElement, currentList); $('#main-content').append(newElement); // Add styles and ids newElement.attr("id", "floatingTocContainer"); newElement.css("padding", "5px"); newElement.css("position", "fixed"); newElement.css("background", "white"); newElement.css("border", "1px solid grey"); newElement.css("right", "0"); newElement.css("top", distanceTop); newElement.css("max-height", distanceBottom); newElement.css("overflow-y", "auto"); newElement.css("z-index", "9999"); newElement.css("font-size", fontSize); newElement.prepend("<div style='float:right;'><a id='showTocLink' href=\"javascript:void(0)\" onclick='javascript:$(\"#floatingToc\").show();$(\"#showTocLink\").hide();$(\"#hideTocLink\").show();return false;'>Show</a><a id='hideTocLink' href=\"javascript:void(0)\" onclick='javascript:$(\"#floatingToc\").hide();$(\"#showTocLink\").show();$(\"#hideTocLink\").hide();return false;'>Hide</a></div>"); currentList.attr("id", "floatingToc"); currentList.css("padding", "0"); currentList.css("list-style-type", "none"); currentList.css("font-size", fontSize); currentList.css("line-height", 1); // Initialize $("#floatingToc").toggle(!initiallyCollapsed); $("#showTocLink").toggle(initiallyCollapsed); $("#hideTocLink").toggle(!initiallyCollapsed); })(); function createEntry(parentElement, parentList) { var hasRealChild = false; parentElement.subElements.forEach(function(entry) { var listItem = $('<li/>', {html: '<a href="#' + entry.id + '">' + entry.text + '</a>'}); // Hide the list icon for helper levels if (entry.id == 'intermediate') { listItem.css("list-style-type", "none"); listItem.css("padding", "0px"); } else { hasRealChild = true; } // Add the subelements if (entry.subElements.length > 0) { var innerList = $('<ul></ul>'); innerList.css("padding-left", "20px"); createEntry(entry, innerList); listItem.append(innerList); } parentList.append(listItem); }); // Check if the subelements contain at least one real child if (hideEmpty && !hasRealChild) { // It doesn't, hide this level completely parentList.css("padding", "0px"); parentList.css("list-style-type", "none"); console.log("bla"); } }
This community is celebrating its one-year anniversary and Atlassian co-founder Mike Cannon-Brookes has all the feels.
Read moreHi Community! Kesha (kay-sha) from the Confluence marketing team here! Can you share stories with us on how your non-technical (think Marketing, Sales, HR, legal, etc.) teams are using Confluen...
Connect with like-minded Atlassian users at free events near you!
Find a groupConnect with like-minded Atlassian users at free events near you!
Unfortunately there are no AUG chapters near you at the moment.
Start an AUGYou're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.