Mendefinisikan template HTML untuk ditambahkan menggunakan JQuery

125

Saya sudah mendapat array yang saya perulangan. Setiap kali kondisi benar, saya ingin menambahkan salinan HTMLkode di bawah ini ke elemen kontainer dengan beberapa nilai.

Di mana saya dapat meletakkan HTML ini untuk digunakan kembali dengan cara yang cerdas?

<a href="#" class="list-group-item">
    <div class="image">
         <img src="" />
    </div>
    <p class="list-group-item-text"></p>
</a>

JQuery

$('.search').keyup(function() {
    $('.list-items').html(null);

    $.each(items, function(index) {
        // APPENDING CODE HERE
    });
});
Patrick Reck
sumber
2
Jika Anda mencari cara yang cerdas, letakkan semua info Anda dalam satu DIV dan beri gaya. Jangan membuat banyak tabel dengan hanya dua sel, jangan membungkusnya menjadi jangkar.
Sergey Snegirev
3
OP jelas tertarik dengan praktik pengkodean yang tepat (pujian!), Jadi saya ingin membantu. Jika saya ingin berkontribusi pada masalah yang sebenarnya, saya akan memposting jawabannya. Saya yakin ada yang setuju bahwa menggunakan tabel dua sel lengkap untuk memposisikan gambar dan beberapa teks hampir tidak dapat dibenarkan kecuali untuk beberapa persyaratan yang benar-benar eksotis (IE4?)
Sergey Snegirev
1
@Brigez_ciparay Mengeposkan komentar tidak benar-benar menyiratkan Anda akan menyimpang. Meskipun saya setuju bahwa komentar adalah tempat yang tepat untuk mereka (selama masih relevan), komentar juga sesuai untuk petunjuk drive-by oleh orang yang belum punya waktu untuk mengembangkannya menjadi jawaban. Ini membantu OP untuk memperjelas apa yang Anda lakukan.
millimoose
Tidak ada yang menjawab pertanyaan Anda, Patrick?
Sebastian Neira

Jawaban:

164

Anda dapat memutuskan untuk menggunakan mesin template dalam proyek Anda, seperti:

Jika Anda tidak ingin menyertakan pustaka lain, John Resig menawarkan solusi jQuery , mirip dengan yang di bawah ini.


Browser dan pembaca layar mengabaikan jenis skrip yang tidak dikenal:

<script id="hidden-template" type="text/x-custom-template">
    <tr>
        <td>Foo</td>
        <td>Bar</td>
    <tr>
</script>

Menggunakan jQuery, menambahkan baris berdasarkan template akan menyerupai:

var template = $('#hidden-template').html();

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});
Mathijs Flietstra
sumber
@MaksimLuzik benar. Mustache (perhatikan ejaan merek dagang) lebih ringan dan akan dicolokkan ke kode OP, tetapi Handlebars memiliki lebih banyak fitur untuk menangani array, dan mungkin memberikan solusi yang lebih memadai pada akhirnya. Berikut artikel perbandingan yang bagus: blog.cubettech.com/…
Michael Scheper
13
Contoh cara mengedit template di sini: jsfiddle.net/meehanman/7vw8bc84
Dean Meehan
175

Pertanyaan lama, tetapi karena pertanyaannya menanyakan "menggunakan jQuery", saya pikir saya akan memberikan opsi yang memungkinkan Anda melakukan ini tanpa memperkenalkan ketergantungan vendor.

Meskipun ada banyak mesin template di luar sana, banyak dari fitur mereka tidak disukai baru-baru ini, dengan iteration ( <% for), conditionals ( <% if) dan transforms ( <%= myString | uppercase %>) dilihat sebagai bahasa mikro paling baik, dan anti-pola paling buruk. Praktik pembuatan template modern mendorong hanya memetakan objek ke representasi DOM (atau lainnya), misalnya apa yang kita lihat dengan properti yang dipetakan ke komponen di ReactJS (terutama komponen stateless).

Template Di Dalam HTML

Satu properti yang dapat Anda andalkan untuk menyimpan HTML untuk template Anda di samping HTML Anda lainnya, adalah dengan menggunakan non-executing <script> type, mis <script type="text/template">. Untuk kasus Anda:

<script type="text/template" data-template="listitem">
    <a href="${url}" class="list-group-item">
        <table>
            <tr>
                <td><img src="${img}"></td>
                <td><p class="list-group-item-text">${title}</p></td>
            </tr>
        </table>
    </a>
</script>

Saat dokumen dimuat, baca template Anda dan beri tokenize menggunakan sederhana String#split

var itemTpl = $('script[data-template="listitem"]').text().split(/\$\{(.+?)\}/g);

Perhatikan bahwa dengan token kami, Anda mendapatkannya dalam [text, property, text, property]format bergantian . Ini memungkinkan kita memetakannya dengan baik menggunakan Array#map, dengan fungsi pemetaan:

function render(props) {
  return function(tok, i) { return (i % 2) ? props[tok] : tok; };
}

Dimana propsbisa terlihat seperti { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Menyatukan semuanya dengan asumsi Anda telah mem-parsing dan memuat Anda itemTplseperti di atas, dan Anda memiliki itemsarray dalam lingkup:

$('.search').keyup(function () {
  $('.list-items').append(items.map(function (item) {
    return itemTpl.map(render(item)).join('');
  }));
});

Pendekatan ini juga hanya sedikit jQuery - Anda harus dapat mengambil pendekatan yang sama menggunakan vanilla javascript dengan document.querySelectordan .innerHTML.

jsfiddle.dll

Template di dalam JS

Sebuah pertanyaan untuk ditanyakan pada diri sendiri adalah: apakah Anda benar-benar ingin / perlu mendefinisikan template sebagai file HTML? Anda selalu dapat membuat komponen + menggunakan kembali template dengan cara yang sama seperti Anda menggunakan kembali sebagian besar hal yang ingin Anda ulangi: dengan sebuah fungsi.

Di es7-land, menggunakan destructuring, template strings, dan arrow-functions, Anda dapat menuliskan fungsi komponen yang tampak cantik yang dapat dengan mudah dimuat menggunakan $.fn.html metode di atas.

const Item = ({ url, img, title }) => `
  <a href="${url}" class="list-group-item">
    <div class="image">
      <img src="${img}" />
    </div>
    <p class="list-group-item-text">${title}</p>
  </a>
`;

Kemudian Anda dapat dengan mudah merendernya, bahkan dipetakan dari array, seperti ini:

$('.list-items').html([
  { url: '/foo', img: 'foo.png', title: 'Foo item' },
  { url: '/bar', img: 'bar.png', title: 'Bar item' },
].map(Item).join(''));

Oh dan catatan terakhir: jangan lupa untuk membersihkan properti Anda yang diteruskan ke template, jika mereka dibaca dari DB, atau seseorang dapat mengirimkan HTML (dan kemudian menjalankan skrip, dll.) Dari halaman Anda.

Josh dari Qaribou
sumber
1
Mesin template yang bagus.
Arthur Castro
41
Template Anda di dalam bagian JS adalah bom
BigRon
2
Bisakah Anda menambahkan demo / biola untuk pendekatan "Templates Inside HTML"?
LCJ
1
Wow! Template di dalam JS ... Jangan pernah memikirkan itu! Luar biasa!
Roman Lopez
2
Wow, ini sangat bagus. Saya menambahkan atribut data-url ke setiap template. Dan mengambil data ajax untuk masing-masing. Ini sangat bagus dan menghemat banyak waktu saya! Terima kasih.
Davey
42

Gunakan template HTML sebagai gantinya!

Karena jawaban yang diterima akan mewakili metode skrip yang kelebihan beban, saya ingin menyarankan yang lain yang, menurut saya, jauh lebih bersih dan lebih aman karena risiko XSS yang datang dengan skrip yang kelebihan beban.

Saya membuat demo untuk menunjukkan kepada Anda bagaimana menggunakannya dalam sebuah aksi dan bagaimana menyuntikkan satu template ke template lainnya, mengedit dan kemudian menambahkan ke DOM dokumen.

contoh html

<template id="mytemplate">
  <style>
     .image{
        width: 100%;
        height: auto;
     }
  </style>
  <a href="#" class="list-group-item">
    <div class="image">
      <img src="" />
    </div>
    <p class="list-group-item-text"></p>
  </a>
</template>

contoh js

// select
var t = document.querySelector('#mytemplate');

// set
t.content.querySelector('img').src = 'demo.png';
t.content.querySelector('p').textContent= 'demo text';

// add to document DOM
var clone = document.importNode(t.content, true); // where true means deep copy
document.body.appendChild(clone);

HTML <template>

  • + Isinya tidak aktif secara efektif sampai diaktifkan. Pada dasarnya, markup Anda adalah DOM tersembunyi dan tidak dirender.

  • + Konten apa pun dalam templat tidak akan memiliki efek samping. Skrip tidak berjalan, gambar tidak dimuat, audio tidak diputar ... hingga templat digunakan.

  • + Konten dianggap tidak ada dalam dokumen. Menggunakan document.getElementById()atau querySelector()di halaman utama tidak akan mengembalikan node turunan dari sebuah template.

  • + Template dapat ditempatkan di manapun dalam <head>, <body>atau <frameset>dan dapat berisi jenis konten yang diperbolehkan dalam elemen-elemen. Perhatikan bahwa "di mana saja" berarti <template>dapat digunakan dengan aman di tempat-tempat yang tidak diizinkan oleh pengurai HTML.

Kembali

Dukungan browser seharusnya tidak menjadi masalah tetapi jika Anda ingin mencakup semua kemungkinan, Anda dapat melakukan pemeriksaan mudah:

Untuk mendeteksi fitur <template>, buat elemen DOM dan periksa apakah properti .content ada:

function supportsTemplate() {
  return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
  // Good to go!
} else {
  // Use old templating techniques or libraries.
}

Beberapa wawasan tentang Metode skrip overloading

  • + Tidak ada yang dirender - browser tidak merender blok ini karena <script>tag memiliki display:nonedefault.
  • + Inert - browser tidak mengurai konten skrip sebagai JS karena tipenya disetel ke selain "text/javascript".
  • -Masalah keamanan - mendorong penggunaan .innerHTML. Penguraian string waktu proses dari data yang disediakan pengguna dapat dengan mudah menyebabkan kerentanan XSS.

Artikel lengkap: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

Referensi yang berguna: https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector

MEMBUAT KOMPONEN WEB Tutorial membuat komponen web kustom menggunakan template HTML oleh Trawersy Media: https://youtu.be/PCWaFLy3VUo

DevWL
sumber
4
The templateelemen tidak didukung oleh Internet Explorer (pada 2018, IE11). Diuji dengan contoh 580 w3c.github.io/html/single-page.html .
Roland
Saya suka <template>, meskipun saya akan merekomendasikan menggunakan kelas secara bebas, jadi jelas alat peraga apa yang diatur ke apa. Masih diperlukan beberapa keterampilan js untuk memetakan data Anda ke dalam template dengan benar, terutama menggunakan pendekatan gaya komponen untuk pemetaan dalam kumpulan data yang besar. Secara pribadi saya masih suka hanya membangun HTML dalam fungsi, atau idealnya membangun DOM langsung dengan JS itu sendiri dan meninggalkan HTML sama sekali (misalnya bagaimana React melakukannya).
Josh dari Qaribou
Pendekatan template bekerja dengan baik untuk saya. Satu rekomendasi yang saya miliki adalah mengkloning template terlebih dahulu (bukan di bagian akhir) dan bekerja dengan objek kloning. Jika tidak, perubahan yang Anda buat terjadi pada template itu sendiri. Jika Anda memiliki logika bersyarat di mana Anda menyetel beberapa properti / konten hanya untuk beberapa waktu, properti tersebut akan ada saat Anda menggunakan template berikutnya. Dengan mengkloning sebelum digunakan, Anda memastikan bahwa Anda selalu memiliki dasar yang konsisten untuk bekerja.
jt.
13

Tambahkan di suatu tempat di tubuh

<div class="hide">
<a href="#" class="list-group-item">
    <table>
        <tr>
            <td><img src=""></td>
            <td><p class="list-group-item-text"></p></td>
        </tr>
    </table>
</a>
</div>

lalu buat css

.hide { display: none; }

dan tambahkan ke js Anda

$('#output').append( $('.hide').html() );
Mateusz Nowak
sumber
1
OP memiliki JSON dan ingin merender beberapa html menggunakan array itu sebagai sumber data. Bagaimana jawaban ini mengatasi masalah? Jawaban Anda mengambil node html dan mengkloning / menduplikasinya dan tidak ada yang berhubungan dengan data binding.
Adrian Iftode
Jadi saya adalah siapa yang downvoting.
Adrian Iftode
Saya benar-benar berpikir Anda tidak membantu mendapatkan jawaban yang lebih baik, karena Anda bisa mulai dengan mengingatkan kami bahwa intinya lebih luas dari apa yang dibahas oleh jawaban kami.
Sebastian Neira
1
Ini memang berhasil, tetapi lebih efisien untuk dilakukan .children().clone(), daripada .html(). Kecepatannya sekitar 3: 1 untuk acak yang <tr/>saya miliki di halaman menggunakan kode ini i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. Rasionya sebenarnya sedikit lebih dibesar-besarkan karena saya menggunakan $ a.clone (), tetapi mencoba mengosongkannya setiap iterasi lebih merupakan kinerja yang sukses daripada kloning, jadi saya tidak yakin bagaimana membuatnya lebih akurat karena fungsi pengaturan waktu memiliki biayanya sendiri.
Chinoto Vokro
1
Pendekatan ini buruk. terutama karena Anda mendapatkan unduhan konten template dan diurai meskipun Anda tidak menggunakannya. Ini masalah yang terlalu besar untuk menganggap ini sebagai jawaban yang valid. Juga seperti yang disebutkan orang lain di atas, jika Anda setidaknya harus menggunakan klon daripada .html ()
DevWL
3

Alternatif lain: Murni

Saya menggunakannya dan itu sangat membantu saya. Contoh yang ditampilkan di situs web mereka:

HTML

<div class="who">
</div>

JSON

{
  "who": "Hello Wrrrld"
}

Hasil

<div class="who">
  Hello Wrrrld
</div>
alditis
sumber
2

Untuk mengatasi masalah ini, saya mengenali dua solusi:

  • Yang pertama berjalan dengan AJAX, yang dengannya Anda harus memuat templat dari file lain dan menambahkan setiap kali Anda mau .clone().

    $.get('url/to/template', function(data) {
        temp = data
        $('.search').keyup(function() {
            $('.list-items').html(null);
    
            $.each(items, function(index) {
                 $(this).append(temp.clone())
            });
    
        });
    });

    Perhatikan bahwa acara tersebut harus ditambahkan setelah ajax selesai untuk memastikan datanya tersedia!

  • Yang kedua adalah menambahkannya secara langsung di mana saja di html asli, pilih dan sembunyikan di jQuery:

    temp = $('.list_group_item').hide()

    Anda bisa setelah menambahkan contoh baru dari template dengan

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • Sama seperti yang sebelumnya, tetapi jika Anda tidak ingin templatenya tetap ada, tetapi hanya di javascript, saya rasa Anda dapat menggunakan (belum mengujinya!) .detach()Daripada menyembunyikan.

    temp = $('.list_group_item').detach()

    .detach() menghapus elemen dari DOM sambil menjaga data dan peristiwa tetap hidup (.remove () tidak!).

Sebastian Neira
sumber
-- Tidak masalah! -
iConnor
Operasi tersebut memiliki JSON dan ingin merender beberapa html menggunakan larik tersebut sebagai sumber data. Bagaimana jawaban ini mengatasi masalah? Jawaban Anda mengambil node html dan mengkloning / menduplikasinya dan tidak ada yang berhubungan dengan data binding.
Adrian Iftode
1
Saya mengutip dari op: Saya ingin menambahkan salinan kode HTML di bawah ini ke elemen kontainer dengan beberapa nilai. Saya yakin ini mengatasi masalahnya.
Sebastian Neira
Dan bagaimana memperlakukan bagian "beberapa nilai"?
Adrian Iftode
Nah, bukan berarti tidak mengatasi masalah tersebut. Saya menghargai bahwa Anda telah memberi tahu saya bahwa saya kehilangan salah satu poinnya :) Bagaimanapun, bagaimana saya tahu cara memasukkan nilai-nilai itu jika saya bahkan tidak tahu nilai apa yang sedang kita bicarakan? Lihat saja bagian ini Di mana saya dapat meletakkan HTML ini untuk digunakan kembali dengan cara yang cerdas? . Apa yang sebenarnya ditanyakan? Tentang JSON atau penyertaan HTML?
Sebastian Neira
1

Jawaban yang sangat bagus dari DevWL tentang penggunaan elemen template HTML5 asli . Untuk berkontribusi pada pertanyaan bagus ini dari OP, saya ingin menambahkan tentang cara menggunakan templateelemen ini menggunakan jQuery, misalnya:

$($('template').html()).insertAfter( $('#somewhere_else') );

Isi dari templatetidak html, tetapi hanya diperlakukan sebagai data, sehingga Anda perlu untuk membungkus konten menjadi objek jQuery untuk mengakses maka metode jQuery.

Jacques
sumber