D
P
0

WordPress & Elementor

Render Konten Halaman Tanpa Wrapper Elementor: Bypass `the_content()` (dan Kenapa `remove_filter` Gagal)

25 Juni 2026·4 menit baca
Render Konten Halaman Tanpa Wrapper Elementor: Bypass `the_content()` (dan Kenapa `remove_filter` Gagal)

Di sebuah situs multi-listing yang saya bangun, ada satu halaman yang harus tampil "mentah". Bukan tanpa alasan estetik — halaman itu menyimpan post_content murni (heading, paragraf, beberapa shortcode), dan klien ingin markup-nya bersih, tanpa div pembungkus Elementor, tanpa kelas elementor-*, tanpa section dan column generator. Masalahnya: begitu Elementor aktif, memanggil the_content() di template menghasilkan output yang sudah dibungkus rapat oleh builder.

Gejalanya tidak melempar error fatal. Tidak ada pesan merah di layar. Yang muncul justru output yang "terlalu rapi":

<div class="elementor elementor-1234" data-elementor-type="wp-page">
  <div class="elementor-section ...">
    <div class="elementor-container ...">
      <!-- konten saya terkubur di sini -->
    </div>
  </div>
</div>

Saya hanya butuh <h2>, <p>, dan hasil shortcode — tanpa lapisan itu. Refleks pertama saya: lepas filter Elementor dari hook the_content.

// Niatnya: copot Elementor dari pipeline konten
remove_filter( 'the_content', [ \Elementor\Plugin::instance()->frontend, 'apply_builder_in_content' ] );

Dan... tidak terjadi apa-apa. Output tetap dibungkus. remove_filter ini sama sekali tidak berefek — no-op total.

Kenapa ini terjadi

remove_filter() di WordPress itu pencocokan yang ketat. Untuk benar-benar melepas sebuah callback dari sebuah hook, tiga hal harus cocok persis: nama hook, callback yang identik, dan prioritas yang sama dengan saat callback itu didaftarkan. Kalau prioritasnya tidak cocok, WordPress tidak menemukan entri yang mau dilepas, dan diam-diam tidak melakukan apa pun.

Di sinilah Elementor menyusahkan. Elementor meng-hook filter the_content pada prioritas tertentu — di beberapa tempat memakai prioritas 9, bukan 10 default. Kalau saya panggil remove_filter tanpa argumen prioritas, default-nya 10, dan pencocokan gagal. Callback tetap terpasang, konten tetap masuk ke apply_builder_in_content, wrapper tetap muncul.

Saya sempat mengejar prioritas yang benar. Tapi di situ saya berhenti dan sadar ini pertarungan yang salah. Bahkan seandainya saya berhasil melepas filter Elementor pada prioritas yang tepat, the_content bukan filter tunggal — ada banyak callback lain yang menempel di sana (formatting, embed, dan filter dari plugin lain). Mencopot satu pemain dari pipeline yang ramai itu rapuh: cukup Elementor ganti prioritas di rilis berikutnya, atau plugin lain ikut menyentuh output, dan perbaikan saya pecah lagi. Saya butuh hasil yang deterministik untuk satu halaman, bukan tebak-tebakan prioritas hook.

Perbaikannya

Daripada melawan filter, saya melewatinya sama sekali. Kuncinya: the_content() itu cuma get_post_field('post_content', ...) yang dijalankan melalui serangkaian filter core. Saya bisa mengambil konten mentahnya langsung, lalu menjalankan ulang pipeline core itu dengan tangan — hanya bagian yang saya mau, tanpa Elementor.

Tapi sebelum menulis kode apa pun, saya verifikasi satu hal dulu: apakah halaman ini benar-benar punya post_content. Ini langkah yang gampang dilewat. Sebagian halaman Elementor menyimpan layout-nya hanya di meta _elementor_data, dan post_content-nya kosong. Kalau itu kasusnya, tidak ada yang bisa dirender dengan cara ini — saya cuma akan dapat string kosong. Saya cek dulu lewat get_post_field, pastikan ada heading dan paragraf asli di sana. Baru lanjut.

function render_konten_mentah( $post_id ) {
    $raw = get_post_field( 'post_content', $post_id );
 
    if ( '' === trim( $raw ) ) {
        return; // post_content kosong: layout ada di _elementor_data, bukan di sini
    }
 
    // Reproduksi pipeline core the_content() secara manual
    $output = $raw;
    $output = do_blocks( $output );    // render blok Gutenberg jika ada
    $output = wptexturize( $output );  // kutip pintar, dash, dll
    $output = wpautop( $output );      // bungkus paragraf jadi <p>
    $output = do_shortcode( $output ); // eksekusi shortcode
 
    echo $output;
}

Lalu saya batasi ini hanya ke halaman yang memang harus mentah — allowlist eksplisit berdasarkan ID. Ini penting: saya tidak mau tanpa sengaja mencopot Elementor dari halaman lain yang justru dibangun penuh dengan builder.

$bypass_ids = [ 142 ]; // hanya halaman ini yang dirender mentah
 
if ( is_page() && in_array( get_the_ID(), $bypass_ids, true ) ) {
    render_konten_mentah( get_the_ID() );
} else {
    the_content(); // halaman lain: biarkan Elementor bekerja seperti biasa
}

Hasilnya persis seperti yang diminta: <h2> dan <p> bersih, shortcode tereksekusi, nol kelas elementor-*. Dan karena allowlist-nya per-ID, sisa situs sama sekali tidak tersentuh — semua halaman builder lain tetap utuh.

Catatan soal urutan: jalankan do_blocks lebih dulu agar markup blok berubah jadi HTML sebelum wpautop mulai menambah tag <p>, supaya paragraf tidak salah bungkus. Sesuaikan langkah mana yang dipakai dengan isi konten Anda — kalau tidak ada blok Gutenberg, do_blocks aman dibiarkan (jadi no-op), tapi saya tetap menuliskannya supaya pipeline-nya setara dengan core.

Pelajaran

Elementor menyadap the_content pada prioritas yang membuat remove_filter rapuh — dan bahkan kalau berhasil lepas, filter lain di pipeline tetap bisa mengubah output. Untuk mengeluarkan satu halaman dari builder, jangan bertarung melawan filter. Ambil post_content mentah lewat get_post_field, jalankan ulang pipeline core (do_blocks, wptexturize, wpautop, do_shortcode) dengan tangan, dan batasi semuanya ke allowlist ID yang eksplisit. Dan selalu verifikasi dulu bahwa post_content memang berisi sesuatu — kalau layout-nya hidup di _elementor_data, tidak ada konten mentah untuk dirender, dan pendekatan ini tidak akan menyelamatkan Anda.