Definiowanie szablonu HTML do dołączenia za pomocą JQuery

125

Mam tablicę, przez którą przechodzę. Za każdym razem, gdy warunek jest spełniony, chcę dołączyć kopię HTMLponiższego kodu do elementu kontenera z pewnymi wartościami.

Gdzie mogę umieścić ten kod HTML w celu ponownego wykorzystania w inteligentny sposób?

<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
źródło
2
Jeśli szukasz sprytnego sposobu, umieść wszystkie informacje w jednym DIV i nadaj mu styl. Nie twórz wielu tabel zawierających tylko dwie komórki, nie zawijaj ich w kotwice.
Sergey Snegirev
3
OP jest oczywiście zainteresowany odpowiednimi praktykami kodowania (brawa!), Więc chciałem pomóc. Gdybym chciał przyczynić się do rzeczywistego problemu, zamieściłbym odpowiedź. Jestem pewien, że każdy zgodzi się, że użycie pełnowymiarowej tabeli z dwiema komórkami do pozycjonowania obrazu i niektórych tekstów jest ledwo uzasadnione, z wyjątkiem niektórych naprawdę egzotycznych wymagań (IE4?)
Sergey Snegirev
1
@BrianG. Opublikowanie komentarza nie oznacza jednak, że idziesz na styczną. Chociaż zgadzam się, że komentarze są odpowiednim miejscem dla tych osób (o ile są nadal aktualne), są one również odpowiednie dla podpowiedzi kierowanych przez osoby, które nie mają jeszcze czasu na rozwinięcie ich w odpowiedzi. OP jest pomocny, jeśli chodzi o wyjaśnienie, co robisz.
millimoose,
Nikt nie odpowiedział na twoje pytanie, Patrick?
Sebastian Neira,

Odpowiedzi:

164

Możesz zdecydować się na użycie silnika szablonów w swoim projekcie, takiego jak:

Jeśli nie chcesz dołączać innej biblioteki, John Resig oferuje rozwiązanie jQuery , podobne do tego poniżej.


Przeglądarki i czytniki ekranu ignorują nierozpoznane typy skryptów:

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

Za pomocą jQuery dodawanie wierszy na podstawie szablonu wyglądałoby następująco:

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

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});
Mathijs Flietstra
źródło
@MaksimLuzik ma rację. Wąsy (zwróć uwagę na pisownię znaku towarowego) są lżejsze i można je podłączyć do kodu OP, ale kierownica ma więcej funkcji do obsługi tablic i może ostatecznie zapewnić bardziej wystarczające rozwiązanie. Oto fajny artykuł porównawczy: blog.cubettech.com/…
Michael
13
Przykład edycji szablonu tutaj: jsfiddle.net/meehanman/7vw8bc84
Dean Meehan
175

Stare pytanie, ale ponieważ pytanie dotyczy „używania jQuery”, pomyślałem, że udostępnię opcję, która pozwoli ci to zrobić bez wprowadzania zależności od dostawcy.

Chociaż istnieje wiele silników tworzących szablony , wiele z ich funkcji zostało ostatnio zniechęconych, a iteracja ( <% for), warunki warunkowe ( <% if) i transformacje ( <%= myString | uppercase %>) są postrzegane w najlepszym przypadku jako mikroling, a w najgorszym - anty-wzorce. Nowoczesne praktyki tworzenia szablonów zachęcają do prostego mapowania obiektu na jego reprezentację DOM (lub inną), np. To, co widzimy z właściwościami odwzorowanymi na komponenty w ReactJS (zwłaszcza komponenty bezstanowe).

Szablony wewnątrz HTML

Jedną z właściwości, na której możesz polegać, aby zachować kod HTML swojego szablonu obok reszty kodu HTML, jest użycie niewykonującego się kodu <script> type, np <script type="text/template">. W Twoim przypadku:

<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>

Podczas ładowania dokumentu przeczytaj swój szablon i stokenizuj go za pomocą prostego pliku String#split

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

Zauważ, że z naszym tokenem otrzymujesz go w naprzemiennym [text, property, text, property]formacie. To pozwala nam ładnie zmapować go za pomocą Array#map, z funkcją mapowania:

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

Gdzie propsmogłoby wyglądać { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Składając to wszystko razem, zakładając, że przeanalizowałeś i załadowałeś swój itemTpljak powyżej i masz itemstablicę w zakresie:

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

To podejście jest tylko ledwie jQuery - powinieneś być w stanie zastosować to samo podejście, używając zwykłego javascript z document.querySelectori .innerHTML.

jsfiddle

Szablony wewnątrz JS

Pytanie, które należy sobie zadać, brzmi: czy naprawdę chcesz / musisz definiować szablony jako pliki HTML? Zawsze możesz skomponować + ponownie użyć szablonu w taki sam sposób, w jaki używałbyś większości rzeczy, które chcesz powtórzyć: za pomocą funkcji.

W es7-land, używając destrukturyzacji, ciągów szablonów i funkcji strzałek, możesz napisać całkiem ładnie wyglądające funkcje składowe, które można łatwo załadować za pomocą $.fn.htmlpowyższej metody.

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>
`;

Wtedy możesz go łatwo wyrenderować, nawet zmapowany z tablicy, na przykład:

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

Aha i ostatnia uwaga: nie zapomnij oczyścić swoich właściwości przekazanych do szablonu, jeśli są odczytywane z bazy danych lub ktoś może przekazać HTML (a następnie uruchomić skrypty itp.) Ze strony.

Josh z Qaribou
źródło
1
Ładny silnik szablonów.
Arthur Castro
41
Twoje szablony w sekcji JS to bomba
BigRon
2
Czy mógłbyś dodać demo / skrzypce dla podejścia „Szablony wewnątrz HTML”?
LCJ
1
Łał! Szablon wewnątrz JS ... Nigdy o tym nie myślałem! Niesamowity!
Roman Lopez
2
Wow, to jest naprawdę bardzo miłe. Dodaję atrybut data-url do każdego szablonu. I pobieranie danych Ajax dla każdego z nich. To takie miłe i zaoszczędziło mi dużo czasu! Dziękuję Ci.
Davey,
42

Zamiast tego użyj szablonu HTML!

Ponieważ zaakceptowana odpowiedź reprezentowałaby metodę skryptową przeciążającą, chciałbym zasugerować inną, która moim zdaniem jest znacznie czystsza i bezpieczniejsza ze względu na ryzyko XSS, które wiąże się z przeciążaniem skryptów.

Zrobiłem demo, aby pokazać, jak użyć go w akcji i jak wstrzyknąć jeden szablon do drugiego, edytować, a następnie dodawać do dokumentu DOM.

przykład 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>

przykład 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 <szablon>

  • + Jego zawartość jest efektywnie obojętna, dopóki nie zostanie aktywowana. Zasadniczo twój znacznik jest ukryty DOM i nie renderuje się.

  • + Żadna zawartość w szablonie nie będzie miała skutków ubocznych. Skrypty nie działają, obrazy się nie ładują, dźwięk nie jest odtwarzany ... do momentu użycia szablonu.

  • + Uważa się, że treść nie znajduje się w dokumencie. Użycie document.getElementById()lub querySelector()na stronie głównej nie zwróci węzłów potomnych szablonu.

  • + Szablonów mogą być umieszczone w dowolnym miejscu wewnątrz <head>, <body>albo <frameset>i może zawierać dowolny rodzaj zawartości, który pozostawia się w tych elementach. Zauważ, że „wszędzie” oznacza, że <template>można go bezpiecznie używać w miejscach, na które parser HTML nie zezwala.

Spadać

Obsługa przeglądarki nie powinna stanowić problemu, ale jeśli chcesz objąć wszystkie możliwości, możesz łatwo sprawdzić:

Aby wykryć funkcje <template>, utwórz element DOM i sprawdź, czy istnieje właściwość .content:

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

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

Kilka spostrzeżeń na temat metody skryptu przeciążania

  • + Nic nie jest renderowane - przeglądarka nie renderuje tego bloku, ponieważ <script>tag ma display:nonedomyślnie.
  • + Inert - przeglądarka nie analizuje zawartości skryptu jako JS, ponieważ jego typ jest ustawiony na coś innego niż "text/javascript".
  • -Kwestie bezpieczeństwa - zachęca do korzystania z .innerHTML. Analiza ciągów znaków w czasie wykonywania danych dostarczonych przez użytkownika może łatwo doprowadzić do luk w zabezpieczeniach XSS.

Cały artykuł: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

Przydatne źródła: https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector

TWORZENIE KOMPONENTÓW INTERNETOWYCH Samouczek dotyczący tworzenia niestandardowych komponentów internetowych przy użyciu szablonów HTML firmy Trawersy Media: https://youtu.be/PCWaFLy3VUo

DevWL
źródło
4
templateElement nie jest obsługiwany przez program Internet Explorer (począwszy od roku 2018, IE11). Przetestowano na przykładzie 580 witryny w3c.github.io/html/single-page.html .
Roland,
Podoba mi się <template>, chociaż zalecałbym swobodne używanie klas, więc jest jasne, jakie rekwizyty są ustawiane na co. Nadal potrzeba pewnych umiejętności js, aby poprawnie odwzorować dane w szablonie, szczególnie przy użyciu podejścia opartego na komponentach do mapowania w dużych zbiorach danych. Osobiście nadal lubię po prostu budować HTML w funkcjach lub najlepiej budować DOM bezpośrednio za pomocą samego JS i całkowicie rezygnować z HTML (np. Jak to robi React).
Josh z Qaribou
Podejście szablonowe działało dobrze dla mnie. Jedno z moich zaleceń to najpierw sklonowanie szablonu (a nie na końcu) i praca z sklonowanym obiektem. W przeciwnym razie zmiany, które wprowadzasz, będą dotyczyły samego szablonu. Gdybyś miał logikę warunkową, w której ustawiałbyś niektóre właściwości / zawartość tylko przez pewien czas, właściwości te byłyby obecne przy następnym użyciu szablonu. Klonując przed każdym użyciem, masz pewność, że zawsze masz spójną podstawę do pracy.
jt.
13

Dodaj gdzieś w ciele

<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>

następnie utwórz css

.hide { display: none; }

i dodaj do swojego js

$('#output').append( $('.hide').html() );
Mateusz Nowak
źródło
1
OP ma JSON i chce wyrenderować część HTML, używając tej tablicy jako źródła danych. Jak ta odpowiedź rozwiązuje problem? Twoja odpowiedź pobiera węzeł html i klonuje / duplikuje go i nie ma nic związanego z wiązaniem danych.
Adrian Iftode,
Więc ja głosuję w dół.
Adrian Iftode,
Naprawdę myślę, że nie pomagasz w uzyskaniu lepszej odpowiedzi, ponieważ mogłeś zacząć od przypomnienia nam, że chodziło o szerszą kwestię niż to, co zostało uwzględnione w naszych odpowiedziach.
Sebastian Neira,
1
To działa, ale jest bardziej wydajne .children().clone()niż .html(). Szybkość wynosi około 3: 1 dla losowej strony, <tr/>którą miałem na stronie używającej tego kodu i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. Współczynnik jest właściwie nieco bardziej przesadzony, ponieważ używam $ a.clone (), ale próba opróżnienia go w każdej iteracji jest bardziej uderzeniem wydajnościowym niż klonowaniem, więc nie jestem pewien, jak zwiększyć dokładność, ponieważ funkcje czasowe mają swój własny koszt.
Chinoto Vokro,
1
To podejście jest złe. głównie dlatego, że pobierasz i analizujesz zawartość szablonu, nawet jeśli jej nie używasz. To zbyt duży problem, aby uznać to za właściwą odpowiedź. Również jako ktoś inny menthon powyżej, jeśli musisz przynajmniej użyć clone zamiast .html ()
DevWL
3

Inna alternatywa: Pure

Używam go i bardzo mi to pomogło. Przykład pokazany na ich stronie internetowej:

HTML

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

JSON

{
  "who": "Hello Wrrrld"
}

Wynik

<div class="who">
  Hello Wrrrld
</div>
alditis
źródło
2

Aby rozwiązać ten problem, poznaję dwa rozwiązania:

  • Pierwsza dotyczy AJAX, za pomocą której będziesz musiał załadować szablon z innego pliku i dodawać go za każdym razem, gdy chcesz .clone().

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

    Weź pod uwagę, że zdarzenie powinno zostać dodane po zakończeniu AJAX, aby mieć pewność, że dane są dostępne!

  • Drugim byłoby dodanie go bezpośrednio w dowolnym miejscu w oryginalnym html, zaznaczenie go i ukrycie w jQuery:

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

    Możesz po dodaniu nowej instancji szablonu za pomocą

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • Tak samo jak poprzedni, ale jeśli nie chcesz, aby szablon tam pozostał, ale tylko w javascript, myślę, że możesz użyć (nie testowałem tego!) .detach()Zamiast hide.

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

    .detach() usuwa elementy z DOM, zachowując przy życiu dane i zdarzenia (.remove () nie!).

Sebastian Neira
źródło
-- Bez problemu! -
iConnor,
Operator ma kod JSON i chce wyrenderować kod HTML, używając tej tablicy jako źródła danych. Jak ta odpowiedź rozwiązuje problem? Twoja odpowiedź pobiera węzeł html i klonuje / duplikuje go i nie ma nic związanego z wiązaniem danych.
Adrian Iftode,
1
Cytuję z op: Chcę dołączyć kopię poniższego kodu HTML do elementu kontenera z pewnymi wartościami. Myślę, że to rozwiązuje jego problem.
Sebastian Neira,
A jak traktowana jest część „niektóre wartości”?
Adrian Iftode,
Cóż, to nie znaczy, że nie rozwiązuje problemu. Cieszę się, że powiedziałeś mi, że brakuje mi jednego z punktów :) W każdym razie, skąd mam wiedzieć, jak wstawić te wartości, jeśli nawet nie wiem, o jakich wartościach mówimy? Wystarczy spojrzeć na tę część. Gdzie mogę umieścić ten kod HTML w celu ponownego wykorzystania w inteligentny sposób? . O co naprawdę się pytają? O dołączeniu JSON czy HTML?
Sebastian Neira,
1

Bardzo dobra odpowiedź od DevWL na temat używania natywnego elementu szablonu HTML5 . Aby przyczynić się do tego dobrego pytania z PO, chciałbym dodać, jak używać tego templateelementu za pomocą jQuery, na przykład:

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

Treść templatenie jest html, ale jest traktowana jako dane, więc musisz opakować zawartość w obiekt jQuery, aby uzyskać dostęp do metod jQuery.

Jacques
źródło