Reading Time with Progress Bar & Sticky Table Of Content Sidebar – Script Code PHP + JS + CSS

Dipublikasikan: 6 Desember 2025

Terakhir diperbarui: 6 Desember 2025

Raymond Kelvin Nando  — Berikut adalah Reading Time & Table Of Content – Script Code PHP,

Anda bisa menempelkan kode dibawah ini di functions.php atau bisa melalui plugins WPCodePro.

Fitur – Fitur :

  • Reading Time
  • Sticky Table of Content
  • Click — Expand H3 & H4
  • TOC Behavior
  • Reading Progress Bar
<?php
if (!is_admin()) {

/* ============================================================= */
/*                    PHP — Reading Time + TOC                    */
/* ============================================================= */

add_filter('the_content', function($content) {

    if (!is_single()) return $content;

    /* === Reading Time === */
    $text = wp_strip_all_tags(get_post_field('post_content', get_the_ID()));
    $word_count = str_word_count($text);
    $minutes = ceil($word_count / 200);

    $reading_time_html = '
        <div id="wpcode-reading-time">
            <span class="rt-clock">⏱</span> '.$minutes.' min read
            <div id="wpcode-progressbar"></div>
        </div>
    ';

    /* === Collect Headings === */
    preg_match_all('/<h([2-4]).*?>(.*?)<\/h[2-4]>/', $content, $matches, PREG_SET_ORDER);

    $toc_html = '';
    if ($matches) {

        $toc_html .= '
            <div id="wpcode-toc">
                <div class="toc-title">Daftar Isi</div>
                <ul class="toc-level toc-lvl-2">
        ';

        $current_lvl = 2;

        foreach ($matches as $i => $match) {

            $lvl = intval($match[1]);
            $title = strip_tags($match[2]);
            $id = "section-".($i+1);

            $content = str_replace(
                $match[0],
                '<h'.$lvl.' id="'.$id.'">'.$match[2].'</h'.$lvl.'>',
                $content
            );

            while ($current_lvl < $lvl) {
                $current_lvl++;
                $toc_html .= '<ul class="toc-level toc-lvl-'.$current_lvl.'">';
            }

            while ($current_lvl > $lvl) {
                $toc_html .= '</ul>';
                $current_lvl--;
            }

            $toc_html .= '
                <li class="toc-item level-'.$lvl.'">
                    <span class="toc-arrow">▸</span>
                    <a href="#'.$id.'">'.$title.'</a>
                </li>
            ';
        }

        while ($current_lvl > 2) {
            $toc_html .= '</ul>';
            $current_lvl--;
        }

        $toc_html .= '</ul></div>';
    }

    return $reading_time_html . $toc_html . $content;
});


/* ============================================================= */
/*                             CSS                                */
/* ============================================================= */

add_action('wp_head', function() { ?>
<style>

#wpcode-progressbar{
    margin-top:6px;height:3px;width:0%;
    background:var(--cs-accent,#0073ff);
    border-radius:2px;
}

/* sidebar desktop */
#wpcode-sidebar-left{
    position:fixed;
    top:120px;
    left:max(0px,calc((100vw - var(--cs-container-width,1140px))/2 - 240px));
    width:240px;
    padding:18px;
    background:#fff;
    border-radius:14px;
    border:1px solid rgba(0,0,0,.08);
    font-size:14px;
    opacity:0;
    animation:fadeIn .4s ease forwards;
    z-index:9999;
}

@keyframes fadeIn{
    from{opacity:0;transform:translateX(45px);}
    to{opacity:1;transform:translateX(15px);}
}

/* TOC */
#wpcode-toc ul{list-style:none;margin:0;padding-left:0;}
#wpcode-toc li{margin:6px 0;display:flex;align-items:center;gap:6px;}

.toc-arrow{font-size:12px;opacity:.6;transition:.2s;cursor:pointer;}

.level-3{padding-left:14px;}
.level-4{padding-left:26px;}

#wpcode-toc a{color:inherit!important;text-decoration:none;}

.toc-item.active > a{
    font-weight:700;
    color:var(--cs-accent,#0073ff)!important;
}

.toc-item.active .toc-arrow{
    transform:rotate(90deg);
    opacity:1;
}

#wpcode-reading-time{margin-bottom:12px;font-weight:600;}


/* ==== AUTO-COLLAPSE H3–H4 ==== */
#wpcode-toc .toc-lvl-3,
#wpcode-toc .toc-lvl-4 {
    display:none;
}

.toc-item.open > ul {
    display:block !important;
}


/* ========================= */
/*   MOBILE MODE              */
/* ========================= */
@media(max-width:992px){

    #wpcode-sidebar-left{display:none!important;}

    #wpcode-mobile-btn{
        position:fixed;
        right:16px;
        bottom:100px;
        background:#0073ff;
        color:#fff;
        padding:10px 14px;
        border-radius:50px;
        font-size:14px;
        font-weight:600;
        box-shadow:0 4px 12px rgba(0,0,0,.25);
        z-index:99999;
        cursor:pointer;
    }

    #wpcode-mobile-panel{
        position:fixed;
        bottom:-100%;
        left:0; right:0;
        height:60%;
        background:#fff;
        border-radius:18px 18px 0 0;
        box-shadow:0 -6px 18px rgba(0,0,0,.18);
        padding:18px;
        overflow-y:auto;
        transition:bottom .35s ease;
        z-index:999999;
    }

    #wpcode-mobile-panel.open{
        bottom:0;
    }
}

</style>
<?php });


/* ============================================================= */
/*                             JS                                 */
/* ============================================================= */

add_action('wp_footer', function() { ?>
<script>
document.addEventListener("DOMContentLoaded", function() {

    const rt  = document.querySelector("#wpcode-reading-time");
    const toc = document.querySelector("#wpcode-toc");

    /* ========== DESKTOP SIDEBAR ========== */
    if (window.innerWidth >= 992) {
        if (rt || toc){
            const sidebar = document.createElement("div");
            sidebar.id = "wpcode-sidebar-left";
            if (rt) sidebar.appendChild(rt);
            if (toc) sidebar.appendChild(toc);
            document.body.appendChild(sidebar);
        }
    }

    /* ========== MOBILE PANEL ========== */
    if (window.innerWidth < 992) {
        if (rt || toc){
            const btn = document.createElement("div");
            btn.id = "wpcode-mobile-btn";
            btn.innerText = "Daftar Isi";
            document.body.appendChild(btn);

            const panel = document.createElement("div");
            panel.id = "wpcode-mobile-panel";
            if(rt) panel.appendChild(rt);
            if(toc) panel.appendChild(toc);
            document.body.appendChild(panel);

            btn.addEventListener("click", ()=> panel.classList.toggle("open"));

            document.addEventListener("click", function(e){
                if (!panel.contains(e.target) && !btn.contains(e.target)){
                    panel.classList.remove("open");
                }
            });
        }
    }


    /* ========== PROGRESS BAR ========== */
    const bar = document.querySelector("#wpcode-progressbar");
    if(bar){
        window.addEventListener("scroll", () => {
            let h = document.documentElement;
            bar.style.width = (h.scrollTop / (h.scrollHeight - h.clientHeight)) * 100 + "%";
        });
    }


    /* ========== TOC BEHAVIOR ========== */
    const headerOffset = 120;
    const headings = document.querySelectorAll("h2[id],h3[id],h4[id]");
    const tocLinks = document.querySelectorAll("#wpcode-toc a");

    /* CLICK — EXPAND H3–H4 */
    tocLinks.forEach(a => {
        a.addEventListener("click", function(e){
            e.preventDefault();

            const id = this.getAttribute("href").substring(1);
            const el = document.getElementById(id);
            if (!el) return;

            const top = el.offsetTop - headerOffset;
            window.scrollTo({ top: top, behavior: "smooth" });

            setTimeout(()=> {
                activate(id);

                const li = this.parentElement;
                li.classList.add("open"); 
            }, 250);
        });
    });

    /* SCROLL SPY */
    window.addEventListener("scroll", () => {
        let scrollPos = window.scrollY + headerOffset + 60;

        headings.forEach(h => {
            if (h.offsetTop <= scrollPos && (h.offsetTop + h.offsetHeight) > scrollPos) {
                activate(h.id);
            }
        });
    });


    /* MARK ACTIVE + EXPAND PARENTS */
    function activate(id){
        document.querySelectorAll(".toc-item").forEach(li => li.classList.remove("active"));

        const active = document.querySelector(`#wpcode-toc a[href="#${id}"]`);
        if (!active) return;

        const li = active.parentElement;
        li.classList.add("active");

        /* AUTO-EXPAND PARENTS */
        let parent = li.parentElement;

        while(parent && parent.id !== "wpcode-toc"){
            if (parent.classList.contains("toc-level")){
                const parentLi = parent.parentElement; 
                if (parentLi.classList.contains("toc-item")){
                    parentLi.classList.add("open");
                }
            }
            parent = parent.parentElement;
        }
    }

});
</script>
<?php });

} 

Untuk tampilan front-endnya bisa disesuaikan pada masing – masing tema anda, Semoga bermanfaat.

Orang lain juga membaca :  Index A-Z Encyclopedia for Category Article - Script Code PHP

Citation

Previous Article

Anonim – Kitab Pararaton [PDF]

Next Article

Index A-Z Encyclopedia for Category Article - Script Code PHP

Citation copied!