WordPress’te İçindekiler Tablosu Otomatik Oluşturma: TOC Fonksiyonu

Blog yazılarında içerik ne kadar zengin olursa olsun, okuyucunun o içeriğe ulaşması zorlaşıyorsa tüm emek boşa gidebilir. Uzun WordPress yazılarında, dokümantasyon sayfalarında ya da ürün açıklama metinlerinde kullanıcılar “acaba bu konuya değinilmiş mi?” diye sayfayı yukarı aşağı kaydırıp duruyor. İşte tam bu noktada İçindekiler Tablosu (Table of Contents / TOC) devreye giriyor ve hem kullanıcı deneyimini hem de SEO performansını ciddi biçimde iyileştiriyor.

Bu yazıda WordPress’te functions.php dosyasına ekleyeceğimiz özel PHP fonksiyonlarıyla TOC’u otomatik olarak nasıl oluşturabileceğimizi, özelleştirebileceğimizi ve yönetebileceğimizi ele alacağız. Hazır eklenti kurmak yerine kendi çözümünüzü yazmak isteyenler için oldukça pratik bir rehber olacak.

Neden Hazır Eklenti Değil, functions.php?

“TOC için zaten onlarca eklenti var, neden uğraşayım?” sorusu aklınıza gelebilir. Haklısınız, ancak her eklenti beraberinde bağımlılık, güncelleme derdi ve performans maliyeti getirir. Özellikle hafif bir tema kullanıyorsanız ve sadece TOC işlevine ihtiyacınız varsa, birkaç satır PHP kodu bu sorunu çok daha temiz çözer.

functions.php üzerinden yapılan çözümün avantajları:

  • Bağımlılık yok: Eklenti güncellemeleri sitenizi bozmaz
  • Tam kontrol: HTML yapısı, CSS sınıfları tamamen sizin elinizde
  • Performans: Ekstra JavaScript dosyası veya veritabanı sorgusu yok
  • Öğrenme: WordPress filter/action mantığını daha iyi anlarsınız

Temel Mantık Nasıl Çalışır?

TOC oluşturmanın özü şu adımlardan oluşur:

  • Post içeriğini alırken the_content filtresini yakala
  • İçerikteki

    ,

    gibi başlık etiketlerini regex ile bul

  • Her başlığa benzersiz bir id attribute ekle
  • Bu başlıkları kullanarak bağlantılı bir liste oluştur
  • Listeyi içeriğin başına ya da istediğin bir yere yerleştir

Basit görünüyor, çünkü gerçekten basit. Haydi koda geçelim.

Adım 1: Temel TOC Fonksiyonu

İlk olarak içerikteki başlıkları tespit eden ve onlara id ekleyen temel fonksiyonu yazıyoruz.

// functions.php içine ekleyin

function generate_toc($content) {
    // Sadece tekil yazı sayfalarında çalıştır
    if (!is_single() && !is_page()) {
        return $content;
    }

    // H2 ve H3 başlıklarını bul
    preg_match_all('/<h([2-3])[^>]*>(.*?)</h1>/i', $content, $matches);

    // Yeterli başlık yoksa TOC oluşturma
    if (count($matches[0]) < 3) {
        return $content;
    }

    $toc = '<div class="toc-container">';
    $toc .= '<div class="toc-title">İçindekiler</div>';
    $toc .= '<ul class="toc-list">';

    $modified_content = $content;
    $used_ids = [];

    foreach ($matches[0] as $index => $heading) {
        $level      = $matches[1][$index];
        $text       = strip_tags($matches[2][$index]);
        $anchor     = sanitize_title($text);

        // Aynı isimde birden fazla başlık varsa sonuna sayı ekle
        if (in_array($anchor, $used_ids)) {
            $anchor = $anchor . '-' . ($index + 1);
        }
        $used_ids[] = $anchor;

        // Başlığa id ekle
        $new_heading = str_replace(
            '<h' . $level,
            '<h' . $level . ' id="' . $anchor . '"',
            $heading
        );
        $modified_content = str_replace($heading, $new_heading, $modified_content);

        // TOC listesine ekle
        $indent = ($level == 3) ? ' class="toc-sub"' : '';
        $toc .= '<li' . $indent . '><a href="#' . $anchor . '">' . $text . '</a></li>';
    }

    $toc .= '</ul></div>';

    return $toc . $modified_content;
}

add_filter('the_content', 'generate_toc');

Bu fonksiyon çalıştığında her

ve

etiketine otomatik id atanır ve sayfanın başına linkleri olan bir liste eklenir.

Adım 2: CSS ile Görünümü Güzelleştirme

TOC’un HTML’i hazır, şimdi bunu güzel göstermek için stil eklememiz gerekiyor. Bunu wp_head aksiyonuna bağlayarak yapabiliriz.

function toc_inline_styles() {
    echo '<style>
    .toc-container {
        background: #f8f9fa;
        border: 1px solid #dee2e6;
        border-left: 4px solid #0073aa;
        border-radius: 4px;
        padding: 20px 25px;
        margin: 30px 0;
        max-width: 600px;
    }
    .toc-title {
        font-weight: 700;
        font-size: 1.1em;
        margin-bottom: 12px;
        color: #333;
    }
    .toc-list {
        list-style: none;
        padding: 0;
        margin: 0;
        counter-reset: toc-counter;
    }
    .toc-list li {
        counter-increment: toc-counter;
        padding: 4px 0;
        border-bottom: 1px dotted #dee2e6;
    }
    .toc-list li:last-child {
        border-bottom: none;
    }
    .toc-list li::before {
        content: counter(toc-counter) ". ";
        color: #0073aa;
        font-weight: 600;
        min-width: 20px;
        display: inline-block;
    }
    .toc-list a {
        color: #555;
        text-decoration: none;
    }
    .toc-list a:hover {
        color: #0073aa;
        text-decoration: underline;
    }
    .toc-sub {
        padding-left: 20px !important;
        font-size: 0.9em;
    }
    .toc-sub::before {
        content: "-- " !important;
        color: #888 !important;
    }
    </style>';
}

add_action('wp_head', 'toc_inline_styles');

Adım 3: Belirli Yazı Türlerinde ve Kategorilerde Çalıştırma

Her sayfada TOC istemeyebilirsiniz. Mesela sadece belirli bir kategorideki yazılarda ya da sadece “Rehber” post type’larında göstermek istiyorsunuz. Bunu kontrol eden bir wrapper ekleyelim.

function generate_toc($content) {
    // Yalnızca ana sorgu döngüsünde çalış
    if (!in_the_loop() || !is_main_query()) {
        return $content;
    }

    // Sadece tekil içeriklerde çalış
    if (!is_singular()) {
        return $content;
    }

    // Belirli post type'larda çalıştır
    $allowed_post_types = ['post', 'page', 'rehber', 'dokumantasyon'];
    if (!in_array(get_post_type(), $allowed_post_types)) {
        return $content;
    }

    // Belirli kategoriyi kontrol et (sadece 'tutorial' kategorisi)
    if (is_single() && !has_category('tutorial') && !has_category('rehber')) {
        // Bu satırı kaldırırsanız tüm yazılarda çalışır
        // return $content;
    }

    // TOC oluşturma kodunun geri kalanı...
    preg_match_all('/<h([2-3])[^>]*>(.*?)</h1>/i', $content, $matches);

    if (count($matches[0]) < 3) {
        return $content;
    }

    // Fonksiyonun geri kalanı aynı kalır
    return $content; // Placeholder
}

Adım 4: TOC’u Shortcode Olarak Kullanmak

Bazen TOC’u içeriğin başına değil, editörde belirlediğiniz bir yere koymak isteyebilirsiniz. Bunun için shortcode yaklaşımı çok daha esnek olur.

function toc_shortcode_handler($atts) {
    global $post;

    $atts = shortcode_atts([
        'title'      => 'İçindekiler',
        'min_headers' => 2,
        'levels'     => '2,3',
    ], $atts, 'toc');

    $content = $post->post_content;

    // Shortcode'u içerikten temizle (sonsuz döngü önlemi)
    $content = preg_replace('/[toc[^]]*]/', '', $content);

    // Seviye konfigürasyonu
    $levels = implode('', explode(',', $atts['levels']));
    $pattern = '/<h([' . $levels . '])[^>]*>(.*?)</h1>/i';

    preg_match_all($pattern, $content, $matches);

    if (count($matches[0]) < intval($atts['min_headers'])) {
        return '';
    }

    $toc = '<div class="toc-container toc-shortcode">';
    $toc .= '<div class="toc-title">' . esc_html($atts['title']) . '</div>';
    $toc .= '<ul class="toc-list">';

    $used_ids = [];

    foreach ($matches[2] as $index => $text) {
        $level  = $matches[1][$index];
        $clean  = strip_tags($text);
        $anchor = sanitize_title($clean);

        if (in_array($anchor, $used_ids)) {
            $anchor .= '-' . ($index + 1);
        }
        $used_ids[] = $anchor;

        $indent = ($level == 3) ? ' class="toc-sub"' : '';
        $toc .= '<li' . $indent . '><a href="#' . esc_attr($anchor) . '">' . esc_html($clean) . '</a></li>';
    }

    $toc .= '</ul></div>';

    return $toc;
}

add_shortcode('toc', 'toc_shortcode_handler');

Artık editörde [toc] yazmanız yeterli. Ya da [toc title="Sayfa Haritası" levels="2,3,4"] şeklinde parametreler verebilirsiniz.

Adım 5: Smooth Scroll Ekleme

Kullanıcı TOC’daki bir bağlantıya tıkladığında sayfa aniden zıplamak yerine yumuşakça kayarsa çok daha iyi bir deneyim sunar.

function toc_smooth_scroll_script() {
    // Sadece TOC olan sayfalarda yükle
    if (!is_singular()) {
        return;
    }
    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function() {
        var tocLinks = document.querySelectorAll('.toc-list a');

        tocLinks.forEach(function(link) {
            link.addEventListener('click', function(e) {
                e.preventDefault();
                var targetId = this.getAttribute('href').substring(1);
                var target   = document.getElementById(targetId);

                if (target) {
                    var offset      = 80; // Sabit header varsa piksel cinsinden yüksekliği
                    var targetPos   = target.getBoundingClientRect().top + window.pageYOffset - offset;

                    window.scrollTo({
                        top: targetPos,
                        behavior: 'smooth'
                    });

                    // URL'yi güncelle ama zıplama
                    history.pushState(null, null, '#' + targetId);
                }
            });
        });
    });
    </script>
    <?php
}

add_action('wp_footer', 'toc_smooth_scroll_script');

Adım 6: “Başa Dön” Linkleri

Uzun yazılarda her başlığın yanına “Başa Dön” linki eklemek kullanıcı deneyimini iyileştirir. Bu özelliği de the_content filtresiyle otomatik hale getirebiliriz.

function add_back_to_top_links($content) {
    if (!is_singular() || !in_the_loop()) {
        return $content;
    }

    // TOC varsa (en az 3 başlık) "başa dön" linklerini ekle
    preg_match_all('/<h[2-3][^>]*>/', $content, $check);
    if (count($check[0]) < 3) {
        return $content;
    }

    $back_link = ' <a href="#" class="back-to-top" title="Başa Dön" aria-label="Başa Dön">↑</a>';

    // Her h2 ve h3 kapanış etiketinden önce link ekle
    $content = preg_replace(
        '/(</h[2-3]>)/i',
        $back_link . '$1',
        $content
    );

    return $content;
}

add_filter('the_content', 'add_back_to_top_links', 20);

Buna karşılık gelen CSS’i de ekleyelim:

function toc_additional_styles() {
    echo '<style>
    .back-to-top {
        font-size: 0.75em;
        margin-left: 10px;
        color: #aaa;
        text-decoration: none;
        vertical-align: middle;
        transition: color 0.2s;
    }
    .back-to-top:hover {
        color: #0073aa;
    }
    /* TOC alanını yazdırma görünümünde gizle */
    @media print {
        .toc-container {
            display: none;
        }
    }
    /* Mobil uyumluluk */
    @media (max-width: 600px) {
        .toc-container {
            max-width: 100%;
            padding: 15px;
        }
    }
    </style>';
}

add_action('wp_head', 'toc_additional_styles');

Adım 7: Meta Box ile Yazı Bazlı TOC Kontrolü

Bazı yazılarda TOC istemeyebilirsiniz. Her yazı için “TOC’u gizle” seçeneği sunan bir meta box ekleyelim.

// Meta box'ı kaydet
function toc_meta_box_save($post_id) {
    if (!isset($_POST['toc_nonce']) || !wp_verify_nonce($_POST['toc_nonce'], 'toc_meta')) {
        return;
    }
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    $hide_toc = isset($_POST['hide_toc']) ? '1' : '0';
    update_post_meta($post_id, '_hide_toc', $hide_toc);
}
add_action('save_post', 'toc_meta_box_save');

// Meta box HTML
function toc_meta_box_html($post) {
    $hide = get_post_meta($post->ID, '_hide_toc', true);
    wp_nonce_field('toc_meta', 'toc_nonce');
    echo '<label>';
    echo '<input type="checkbox" name="hide_toc" value="1" ' . checked($hide, '1', false) . '>';
    echo ' Bu yazıda İçindekiler Tablosunu gizle';
    echo '</label>';
}

// Meta box'ı ekrana getir
function toc_add_meta_box() {
    add_meta_box(
        'toc_settings',
        'TOC Ayarları',
        'toc_meta_box_html',
        ['post', 'page'],
        'side',
        'default'
    );
}
add_action('add_meta_boxes', 'toc_add_meta_box');

Bu meta box’ı generate_toc fonksiyonunuza entegre etmek için fonksiyonun başına şunu ekleyin:

// generate_toc fonksiyonunun içinde, en başa ekleyin:
global $post;
if (get_post_meta($post->ID, '_hide_toc', true) === '1') {
    return $content;
}

Gerçek Dünya Senaryosu: WooCommerce Ürün Açıklamaları

WooCommerce kullanıyorsanız uzun ürün açıklamalarında da TOC işinize yarayabilir. Özellikle teknik ürünlerde, “Teknik Özellikler”, “Kurulum”, “Garanti” gibi bölümlere hızlı erişim sağlamak dönüşüm oranlarını artırabilir.

function woo_product_toc($content) {
    // Sadece WooCommerce ürün sayfalarında çalış
    if (!is_product()) {
        return $content;
    }

    preg_match_all('/<h([2-4])[^>]*>(.*?)</h1>/i', $content, $matches);

    if (count($matches[0]) < 2) {
        return $content;
    }

    $toc = '<div class="toc-container toc-product">';
    $toc .= '<div class="toc-title">Hızlı Erişim</div>';
    $toc .= '<ul class="toc-list">';

    $used_ids = [];

    foreach ($matches[0] as $index => $heading) {
        $level  = $matches[1][$index];
        $text   = strip_tags($matches[2][$index]);
        $anchor = sanitize_title($text);

        if (in_array($anchor, $used_ids)) {
            $anchor .= '-' . ($index + 1);
        }
        $used_ids[] = $anchor;

        $new_heading  = str_replace('<h' . $level, '<h' . $level . ' id="' . $anchor . '"', $heading);
        $content      = str_replace($heading, $new_heading, $content);

        $toc .= '<li><a href="#' . esc_attr($anchor) . '">' . esc_html($text) . '</a></li>';
    }

    $toc .= '</ul></div>';

    return $toc . $content;
}

add_filter('the_content', 'woo_product_toc');

SEO Açısından TOC’un Önemi

Google, doğru implement edilmiş TOC’ları arama sonuçlarında sitelink olarak gösterebiliyor. Bu, organik tıklama oranınızı ciddi biçimde artırabilir. Bunun için yapmanız gerekenler:

Ayrıca çok uzun içeriklerde TOC, kullanıcıların sayfada daha uzun süre kalmasını sağlar. Bu da dolaylı olarak bounce rate’i düşürür ve Google’ın gözünde içeriğin kalitesi hakkında olumlu sinyal verir.

Hata Ayıklama İpuçları

Fonksiyonları ekledikten sonra TOC görünmüyorsa şunları kontrol edin:

  • is_single() veya is_singular() kontrolü: Yazı arşivlerinde, ana sayfada çalışmaz, bu normal
  • Minimum başlık sayısı: Kodda < 3 kontrolü var, yazınızda 3’ten az başlık varsa TOC çıkmaz
  • Tema çakışması: Temanızın the_content filtresini geçersiz kılıp kılmadığını has_filter('the_content', 'generate_toc') ile test edin
  • Cache sorunu: W3 Total Cache veya WP Super Cache kullanıyorsanız önbelleği temizleyin
  • HTML yapısı: Editörde başlıkların gerçekten

    etiketi olarak kaydedildiğini, özellikle Classic Editor kullanıyorsanız Visual moddan çıkıp Text modda kontrol edin

Sonuç

WordPress functions.php dosyasına birkaç yüz satır kod ekleyerek tam özellikli, özelleştirilebilir ve performanslı bir TOC sistemi kurabilirsiniz. Bu yazıda ele aldığımız yaklaşım size şunları sağladı:

  • Otomatik başlık tespiti ve ID atama
  • Shortcode desteği ile esnek yerleştirme
  • Smooth scroll ile gelişmiş UX
  • “Başa Dön” bağlantıları
  • Yazı bazlı meta box kontrolü
  • WooCommerce ürün uyumluluğu

Bu çözümü kendi projenize göre adapte etmek için önce temel fonksiyonla başlayın, çalıştığını doğrulayın, sonra ihtiyaçlarınıza göre katmanları ekleyin. Hazır eklenti kurup ne yaptığını bilmemek yerine, kendi yazdığınız kodu anlamak ve kontrol etmek uzun vadede çok daha sürdürülebilir bir yaklaşım.

Herhangi bir adımda takılırsanız veya farklı bir post type için uyarlama yapmak isterseniz yorumlarda belirtin, birlikte bakarız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir