Cách thêm mục lục (table of content) cho mô tả danh mục sản phẩm

Cập nhật lần cuối 24/10/2023 by trong WordPress vào 04/09/2023 có 1543 Views

Đa số các plugin toc+ hay table of content đang có đều không hỗ trợ mục lục cho mô tả danh mục sản phẩm nên mình đã tự viết + tham khảo thêm chat GPT, cuối cùng đã viết được mục lục cho mô tả danh mục sản phẩm rồi. Chia sẻ cùng các bạn sử dụng luôn nếu cần nhé

Các bạn chỉ cần copy đoạn code sau vào functions.php của theme đang sử dụng (wp-content/themes/{your-theme}/functions.php) là được. Áp dụng cho mô tả danh mục sản phẩm với mọi theme có sử dụng Woocommerce nhé

/*
* Thêm mục lục cho mô tả danh mục sản phẩm
* Author: levantoan.com
*/
add_action('wp_footer', 'devvn_add_heading_style');
function devvn_add_heading_style(){
    ?>
    <style>
        .devvn_heading_wrap {
            background: rgba(243,243,243,0.95);
            border: 1px solid rgb(197 197 197);
            width: 100%;
            color: #333;
            margin: 0 0 20px 0;
            padding: 0;
        }
        .devvn_heading_title {
            padding: 10px 15px;
            background: #c5c5c5;
            position: relative;
            cursor: pointer;
        }
        .devvn_heading_wrap > ol {
            margin: 10px 0 10px 33px;
        }
        .devvn_heading_wrap > ol > li {
            margin: 0;
            padding: 0;
        }
        .devvn_heading_wrap > ol > li ul {
            margin: 0;
            padding: 0;
        }
        .devvn_heading_wrap > ol > li ul li {
            margin: 0;
            padding: 0;
            list-style: none;
        }
        .devvn_heading_wrap > ol {
            counter-reset: List;
        }
        .devvn_heading_wrap > ol > li {
            counter-increment: List;
        }
        .devvn_heading_wrap > ol > li ul {
            counter-reset: List;
        }
        .devvn_heading_wrap > ol > li ul li {
            counter-increment: List;
        }
        .devvn_heading_wrap > ol > li ul li:before {
            content: counters(List,".");
        }
        .devvn_heading_title strong {
            background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAACYktHRAAAqo0jMgAAAAlwSFlzAAAAYAAAAGAA8GtCzwAAAAd0SU1FB+cJBA8RMDu7ID0AAAKXSURBVFjD7ZVNSBtBGIa/mV1Nfw6BtgoVidp6KPbSZj20CppCL6WHeqhgTDZGCIJSUGOQlGiagElIicHUQ+qaQGx0D+mp0EOhPyAoQssuCqXXUkIoVKWtFamszfQQTKKbg4fJyTynmXkG3vmGbxiACqcd1PxMo9EmfD4Y6+kBRlGIThDktmAQIJsFAGhJVVef/eHxkOs8jxz19VRS7ZkM3BfF3y8mJ5H+y/Q04u32Yk+CIyPy3UgEAED/aGoKrblcZSnfEgphaO7tVV3LX7M5P+lSe1qQQbMZw+rBgcowipI/zM8SntoJFAUjQzyuuoFALJaf7Kk9NTZiMdTdzTBf342NEavJRAz7+8grCJIcjwMQktuFcWvd8HBWsFjQdkMDleD1dJp8W1y8yoTDZSuuwklBnL6qCgYmJuCi0QiMohBhfl5+E4kc9kAnYdnduNNJdDyPvtfUUEk9t7UFD0UROJ8PcVwgAOB0Ht0xNCRJ0SgAgP6j240Gvd6ylL/q92OAvr7j6+SD1Zqf7BSNadPe34/h9uFzK4DuFNbQeO5PKAv2bBbDtYUFlXiSSBROU8JTgqwlErkmlFwu8tZohH+KglyCIMmzs8VN+IcfHwfBYkEva2upBF/e3ISUKCLZ7y9XcRVODOokLLvb6nBAh9EIWFFIx9yc/CAWK/6MOG50FNw8D790Oiqp6+k03BDFK69DIcQ993ph3u0+smHKZpPu5b5hjnM6AQKBclRPbno8GD7bbCrzvmiNlPC0MNlsmIQxVgkDy+bzn5bwtGhnWYxuLS2phDuZzI8vlPCUQHvJJHP+8fLymbBGA5caG0nX9jZqCQal6MzMYRPWfVpZIW0YQ6qpCcW1WirJ9kyGDESjO6+O9V6FU8l/eGH2Ze896egAAAAASUVORK5CYII=) no-repeat left center;
            background-size: 22px 22px !important;
            padding-left: 32px;
            text-transform: uppercase;
        }
        .devvn_heading_title::after {
            background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAACYktHRAAAqo0jMgAAAAlwSFlzAAAAYAAAAGAA8GtCzwAAAAd0SU1FB+cJBA8REu7bYdkAAAHLSURBVFjD7ZS9S8NAGId/l0a0QaiuDl3cXIScg2CrRQhSuruKf4GggthSN1P6KYKTi7vtVmIpQehgFzHFycmPQfBjakGpIsI5SEtr05qmaQXJD27J+949T94jAezYsQNggfE8FV2uQfGm2djYAuN5AOBoRZJeis/PIJWK+JBOUyoI/QJTKgjiYSbDz5TLL59PT7QiSYSK19cgk5O1JqYWCkQKBDStWrUazlRFIZLPV3/Ibm44oHn0RPL5mKooVk5CFw4AcLk4cheP/9xgpUR7OIC1RIK7KMdiKMqyngSQz08dj472AoeUzerB2dLenrYSjZJ68/vuLuaCwdZjzs7etvz+q+XXVzNwqIuLevCSvL4OAKRpk0USRuEtAlZIdAPXFehFolt4WwEzEmbgAMC1E9BGQiGsRiKtFY/HGc3lGr8Os3AAcKBDHk9OTydCw8O49HqbK273UHF+fnw/nXbf8rxZeMcraBqvmEiAbGy0ANRCAaj9M34Wk0mttLn529mGBACAHsgyjra3jfQaefNaHEaagE7XYR7elYARiW7hpkMPZJlSxhqXGEyl+g5ujHi+s0Ppx8f3CocHCq9PggrC7L3T+SdwO3b+Tb4AHn8FI1FdY6oAAAAASUVORK5CYII=) no-repeat center center;
            width: 12px;
            height: 12px;
            background-size: 100% 100%;
            right: 15px;
            top: 50%;
            margin-top: -6px;
            content: "";
            position: absolute;
            transition: all .3s linear;
            -moz-transition: all .3s linear;
            -webkit-transition: all .3s linear;
        }
        .devvn_heading_wrap.active .devvn_heading_title::after {
            transform: rotate(180deg);
            -moz-transform: rotate(180deg);
            -webkit-transform: rotate(180deg);
        }
    </style>
    <script>
        (function ($){
            $('body').on('click', '.devvn_heading_title', function (){
                let thisBox = $(this).closest('.devvn_heading_wrap');
                if(thisBox.hasClass('active')){
                    $('> ol', thisBox).slideUp();
                }else{
                    $('> ol', thisBox).slideDown();
                }
                thisBox.toggleClass('active');
            });
            $('.devvn_heading_wrap a').on('click', function (){
                let idElement = $(this).attr("href");
                let top = $(idElement).offset().top;
                $('html, body').animate({scrollTop:top-44}, 500 );
                return false;
            })
        })(jQuery);
    </script>
    <?php
}

add_filter('woocommerce_taxonomy_archive_description_raw', 'devvn_auto_add_heading_ids');
function devvn_auto_add_heading_ids($content) {
    $content = wp_kses_post( $content );
    $dom = new DOMDocument();
    if(!$content) return $content;
    $dom->loadHTML(mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8'));
    $xpath = new DOMXPath($dom);
    $headings = $xpath->query('//h2|//h3');
    $table_content = array();
    $h2 = 0;
    foreach ($headings as $index => $heading) {
        $headingText = $heading->textContent;
        $id = sanitize_title($headingText);
        $id = ensure_unique_id($id, $xpath, $index);
        $heading->setAttribute('id', $id);
        if($heading->tagName == 'h2') {
            $h2++;
            $table_content[$h2] = array(
                'title' => $headingText,
                'href' => '#'.$id,
            );
        }elseif($heading->tagName == 'h3') {
            $table_content[$h2]['child'][] = array(
                'title' => $headingText,
                'href' => '#'.$id,
            );
        }
    }
    $newContent = $dom->saveHTML();
    $table_content_out = '';
    if($table_content){
        ob_start();
        ?>
        <div class="devvn_heading_wrap active">
            <div class="devvn_heading_title">
                <strong><?php _e('Mục lục', 'devvn');?></strong>
            </div>
            <ol>
                <?php foreach ($table_content as $item):?>
                    <?php
                        $title = isset($item['title']) ? sanitize_text_field($item['title']) : '';
                        $href = isset($item['href']) ? esc_attr($item['href']) : '';
                        $child = isset($item['child']) ? (array) $item['child'] : array();
                        if(!$title) continue;
                    ?>
                    <li><a href="<?php echo $href;?>" title="<?php echo $title;?>"><?php echo $title;?></a><?php if($child){?><ul><?php foreach ($child as $child_item):?>
                            <?php
                            $child_title = isset($child_item['title']) ? sanitize_text_field($child_item['title']) : '';
                            $child_href = isset($child_item['href']) ? esc_attr($child_item['href']) : '';
                            if(!$child_title) continue;
                            ?>
                            <li>
                                <a href="<?php echo $child_href;?>" title="<?php echo $child_title;?>"><?php echo $child_title;?></a>
                            </li>
                        <?php endforeach;?></ul><?php }?></li>
                <?php endforeach;?>
            </ol>
        </div>
        <?php
        $table_content_out = ob_get_clean();
    }
    return $table_content_out . $newContent;
}

if(!function_exists('ensure_unique_id')) {
    function ensure_unique_id($id, $xpath, $index)
    {
        $existingIds = [];
        $existingHeadings = $xpath->query('//h2[@id] | //h3[@id]');
        foreach ($existingHeadings as $existingHeading) {
            $existingId = $existingHeading->getAttribute('id');
            $existingIds[] = $existingId;
        }
        if (in_array($id, $existingIds)) {
            $i = 1;
            $newId = $id . '-' . $i;
            while (in_array($newId, $existingIds)) {
                $i++;
                $newId = $id . '-' . $i;
            }
            return $newId;
        }
        return $id;
    }
}

Hiện tại code trên hỗ trợ cho thẻ H2 và H3 nha. Có style cơ bản rồi. Các bạn tự style lại cho đẹp nếu muốn nhé

Để duy trì blog nên mình có làm aff cho 1 số bên hosting. Nhưng dù aff mình cũng chọn 1 số nhà cung cấp uy tín về chất lượng và support nên các bạn cứ yên tâm nhé.

Nếu có mua hosting mà có trong list dưới đây các bạn click vào link trước khi mua để ủng hộ mình nhé. Mình cảm ơn nhiều

Chúc các bạn thành công!

4.2/5 - (5 votes)
  • Bình luận
Sản phẩm nổi bật của Toản
x