• As I’m new to the WordPress plugin atmosphere and how to support free plugins, I think this chat might be the easiest. When this is supposed to be done differently please let me know.

    The below code will highlight the TableOfContent widget links based on the scroll behaviour of the user. When the user scrolls through your article, the top heading will be highlighted (by assigning the ‘activeScroll’ class to the TOC Link).

    The full code implementation can be seen in this Pull Request.
    The code is working on this blog.

    Below I’ll drop the main library which performs the logic. Feel free to re-use or implement in this lovely plugin!

    Last remark: this was implemented together with the 2W3 Fixed Widget to make the TOC sticky to the top.

    $ = jQuery;
    $( document ).ready( function(){
    	if( $( "div.widget_lpwtoc_widget" ).length ){
    		// Function to set the headingObject easily by index
    		var getHeadingObj = function( index ){
    			return ( ( index < 0 )
    					? { offset: 0 }
    					: { index: index, offset: headerArray[ index ].offset } );
    		}
    
    		// To avoid incorrect behaviour, some offset was defined to work nicely with a sticky header on top of page
    		var PREVIOUS_SCROLL_OFFSET = 150;
    
    		// Construct the header Array
    		var headerArray = new Array();
    		$(":header span").each( function(){ headerArray.push( { offset: $( this).offset().top - PREVIOUS_SCROLL_OFFSET, ref: this.id } ); } );
    
    		// Initiate the headings
    		var prevHeading;
    		var currHeading;
    		var nextHeading = getHeadingObj( 0 );
    
    		var lastScrollPos = 0;
    
    		$(window).scroll(function(){
    			let scrollPos = $(window).scrollTop();
    			
    			// Scrolling down, check next heading - as long as this is not the last
    			if( scrollPos > lastScrollPos && nextHeading && scrollPos >= nextHeading.offset ){
    				// shift all objects
    				prevHeading = currHeading;
    				currHeading = nextHeading;
    
    				// set next heading, when current is not the last one
    				let nextHeadingIndex = ( !currHeading || currHeading.index == headerArray.length - 1 ) ? undefined : currHeading.index + 1;
    				nextHeading = ( nextHeadingIndex ) ? getHeadingObj( nextHeadingIndex ) : undefined;
    
    			} else if( scrollPos < lastScrollPos && currHeading && scrollPos <= currHeading.offset ){
    				// Scrolling up, check previous heading - as long as the current is not the first;
    				nextHeading = currHeading;
    				currHeading = prevHeading;
    				
    				// set next heading, when current is not the last one
    				let prevHeadingIndex = ( !currHeading || currHeading.index == 0 ) ? undefined : currHeading.index - 1;
    				prevHeading = ( prevHeadingIndex ) ? getHeadingObj( prevHeadingIndex ) : undefined;
    			
    			}
    			
    			// update select class
    			$( ".lwptoc_item a" ).removeClass( 'activeScroll' );
    			if( currHeading != undefined && currHeading.index >= 0 ){
    				$(".lwptoc_item a[href='#"+ headerArray[ currHeading.index ].ref +"']").addClass( 'activeScroll' );
    			}
    			
    			// update lastScrollPos to allow a bit better performance (not fetching next and prev heading)
    			lastScrollPos = scrollPos;
    		});
    	}
    });

    The page I need help with: [log in to see the link]

Viewing 6 replies - 1 through 6 (of 6 total)
  • Plugin Author LuckyWP

    (@theluckywp)

    Hello @fox_creation !
    Thank you for this solution. We try make this in one of future versions.

    Hi @fox_creation,

    I already tried your version, but it has some bugs:
    1. if the TOC depth is less than the heading levels in the articles, then sometimes it will not show any “active state”.
    2. it doesn’t work well if Google Adsense is loaded.

    so I already try another modified JS from https://css-tricks.com/sticky-table-of-contents-with-scrolling-active-states/.

    The CSS:

    .lwptoc_item a.activeScroll{
    	font-weight: bold;
    	border-left: 2px solid red;
    	padding-left: 3px;
    	margin-left: -5px;
    	display: block;
    }

    The JS:

    
    <!-- TOC highlights active state -->
    <!-- https://css-tricks.com/sticky-table-of-contents-with-scrolling-active-states/ -->
    <script>	
    	$ = jQuery;
    	$(document).ready( function(){
    		const observer = new IntersectionObserver(entries => {
    			entries.forEach(entry => {
    				// get span <code>id</code>
    				const id = entry.target.getAttribute('id');
    				if (id.length){
    					var showCurrentTOC = $(".lwptoc_item a[href='#"+ id +"']");
    					// check if id exist in TOC 
    					if (entry.intersectionRatio > 0 && showCurrentTOC.length) {
    						// clear all active class on TOC then apply a class on selected id
    						$(".lwptoc_item a").removeClass( 'activeScroll' );
    						showCurrentTOC.addClass( 'activeScroll' );			
    						
    						//scroll to current TOC list
    						$(".widget_lpwtoc_widget").scrollTop($(".widget_lpwtoc_widget").scrollTop() + showCurrentTOC.position().top - ($(".widget_lpwtoc_widget").height()/2) );
    					} else {
    					}						
    				}
    			});
    		});
    
    		// Track all span that have an <code>id</code> applied
    		document.querySelectorAll('span[id]').forEach((span) => {
    			observer.observe(span);
    		});
    
    	});
    </script>	
    

    Hi @theluckywp,
    also, I already add function to scroll into the current TOC list after adding an active class.

    please check it on my website

    known bug:
    I know this method is not the best, because the active state is updated when any heading is displayed. So, when scrolling from the bottom, TOC active state may not be updated to the previous heading until it displayed.

    As far as I know, it is better to use section tag on heading and its content. But it need to append sections in all the heading and its content.

    • This reply was modified 3 years, 10 months ago by Philip Faster. Reason: modify css
    • This reply was modified 3 years, 10 months ago by Philip Faster.
    • This reply was modified 3 years, 10 months ago by t-p.

    @philipfaster how did you get this working? It looks great on your website.

    I uploaded the js Reiner refered to:
    https://github.com/foxcreation/FOXY_solutions/blob/master/wp-content/themes/foxy-illdy/layout/js/tableOfContentActiveScroll.js

    I then added the functions.php script to my child theme:
    https://github.com/foxcreation/FOXY_solutions/blob/e897070d8d3e0050238158dcce4e553d5bc644c1/wp-content/themes/foxy-illdy/functions.php

    Then I added your CSS style to my child theme in your post above.

    But it doesnt highlight on active state. What am I doing wrong?

    Thread Starter Reinier van den Assum

    (@fox_creation)

    @etribalnick as mentioned in my email reply you’ve checked almost all boxes uploading the JS, adding the functions.php script and css. Please double check whether all documents are added inside the Child-theme to prevent any overrides per Theme upgrade and that the references are correct and working (you should see the tableOfContentActiveScroll.js in your Developer Tools > Network tab and see the content of the file.

    In case of any other questions or comments, please feel free to reach out.

    hi @etribalnick, you may attach a link you working with. So we may check the trouble you encounter.

    Thread Starter Reinier van den Assum

    (@fox_creation)

    Please be aware of a bug in the original code snippet (I can’t edit the post), causing the first heading to miss some highlight. This was caused because of index 0 resulting in a falsify ( index ) => false when index == 0.

    This has been fixed in https://github.com/foxcreation/FOXY_solutions/pull/20/files checking Number.isInteger( index ) which will be true for index == 0.
    FYI @philipfaster @etribalnick @theluckywp

Viewing 6 replies - 1 through 6 (of 6 total)
  • The topic ‘Contribution: Show actively watched heading’ is closed to new replies.