Definizione di un modello HTML da aggiungere utilizzando JQuery


125

Ho un array che sto scorrendo. Ogni volta che una condizione è vera, voglio aggiungere una copia del HTMLcodice seguente a un elemento contenitore con alcuni valori.

Dove posso inserire questo codice HTML per riutilizzarlo in modo intelligente?

<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
    });
});

2
Se stai cercando un modo intelligente, metti tutte le tue informazioni in un unico DIV e modellalo. Non creare numerose tabelle con solo due celle, non avvolgerle in ancore.
Sergey Snegirev

3
OP è ovviamente interessato a pratiche di codifica adeguate (complimenti!), Quindi ho voluto aiutare. Se volessi contribuire al problema reale, posterò una risposta. Sono sicuro che qualcuno concorda sul fatto che l'uso di una tabella a due celle in piena regola per posizionare un'immagine e un po 'di testo è a malapena giustificato tranne che per alcuni requisiti davvero esotici (IE4?)
Sergey Snegirev

1
@BrianG. La pubblicazione di un commento non implica però che stai andando su una tangente. Anche se sono d'accordo sul fatto che i commenti siano un luogo appropriato per coloro (purché siano ancora rilevanti), sono anche appropriati per i suggerimenti di persone che non hanno ancora il tempo di espanderli in una risposta. È utile per l'OP chiarire cosa stai facendo.
millimoose

Nessuno ha risposto alla tua domanda, Patrick?
Sebastian Neira

Risposte:


164

Potresti decidere di utilizzare un motore di modelli nel tuo progetto, come ad esempio:

Se non vuoi includere un'altra libreria, John Resig offre una soluzione jQuery , simile a quella qui sotto.


I browser e gli screen reader ignorano i tipi di script non riconosciuti:

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

Utilizzando jQuery, l'aggiunta di righe in base al modello sarebbe simile a:

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

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});

@MaksimLuzik ha ragione. Moustache (nota l'ortografia del marchio) è più leggero e si collegherà al codice dell'OP, ma Handlebars ha più funzionalità per la gestione degli array e può fornire una soluzione più sufficiente alla fine. Ecco un bell'articolo di confronto: blog.cubettech.com/…
Michael Scheper

13
Esempio di come modificare il modello qui: jsfiddle.net/meehanman/7vw8bc84
Dean Meehan

175

Vecchia domanda, ma poiché la domanda chiede "utilizzando jQuery", ho pensato di fornire un'opzione che ti consente di farlo senza introdurre alcuna dipendenza dal fornitore.

Sebbene ci siano molti motori di modellazione là fuori, molte delle loro caratteristiche sono cadute in disgrazia di recente, con iterazione ( <% for), condizionali ( <% if) e trasformazioni ( <%= myString | uppercase %>) viste come microlinguaggio nella migliore delle ipotesi e anti-pattern nel peggiore dei casi. Le moderne pratiche di templating incoraggiano semplicemente la mappatura di un oggetto sulla sua rappresentazione DOM (o altra), ad esempio ciò che vediamo con proprietà mappate sui componenti in ReactJS (specialmente componenti stateless).

Modelli in HTML

Una proprietà su cui puoi fare affidamento per mantenere l'HTML per il tuo modello accanto al resto del tuo HTML, è l'utilizzo di un non-eseguibile <script> type, ad es <script type="text/template">. Per il tuo caso:

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

Al caricamento del documento, leggi il tuo modello e tokenizzalo usando un semplice file String#split

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

Nota che con il nostro token, lo ottieni nel [text, property, text, property]formato alternativo . Questo ci permette di mapparlo bene usando un Array#map, con una funzione di mappatura:

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

Dove propspotrebbe assomigliare { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Mettendo tutto insieme supponendo che tu abbia analizzato e caricato itemTplcome sopra, e hai un itemsarray nell'ambito:

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

Questo approccio è anche solo appena jQuery: dovresti essere in grado di adottare lo stesso approccio usando javascript vanilla con document.querySelectore .innerHTML.

jsfiddle

Modelli all'interno di JS

Una domanda da porsi è: vuoi veramente / hai bisogno di definire i modelli come file HTML? Puoi sempre componentizzare + riutilizzare un modello nello stesso modo in cui riutilizzeresti la maggior parte delle cose che vuoi ripetere: con una funzione.

In es7-land, usando la destrutturazione, le stringhe di template e le funzioni freccia, puoi scrivere delle funzioni componenti decisamente carine che possono essere facilmente caricate usando il $.fn.htmlmetodo sopra.

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

Quindi potresti facilmente renderlo, anche mappato da un array, in questo modo:

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

Oh e nota finale: non dimenticare di disinfettare le tue proprietà passate a un modello, se vengono lette da un DB, o qualcuno potrebbe passare in HTML (e quindi eseguire script, ecc.) Dalla tua pagina.


1
Bel motore di modelli.
Arthur Castro

41
I tuoi modelli nella sezione JS sono la bomba
BigRon

2
Potresti aggiungere una demo / violino per l'approccio "Templates Inside HTML"?
LCJ

1
Wow! Modello all'interno di JS ... Non ci avevo mai pensato! Sorprendente!
Roman Lopez

2
Wow, questo è davvero molto carino. Sto aggiungendo un attributo data-url a ogni modello. E il recupero dei dati ajax per ciascuno. Questo è così bello e mi ha fatto risparmiare così tanto tempo! Grazie.
Davey

42

Usa invece il modello HTML!

Poiché la risposta accettata rappresenterebbe il metodo di sovraccarico degli script, vorrei suggerirne un altro che è, a mio parere, molto più pulito e sicuro a causa dei rischi XSS che derivano dal sovraccarico degli script.

Ho realizzato una demo per mostrarti come usarlo in un'azione e come inserire un template in un altro, modificarlo e poi aggiungerlo al documento DOM.

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

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

  • + Il suo contenuto è effettivamente inerte fino all'attivazione. Essenzialmente, il tuo markup è nascosto DOM e non esegue il rendering.

  • + Qualsiasi contenuto all'interno di un modello non avrà effetti collaterali. Gli script non vengono eseguiti, le immagini non vengono caricate, l'audio non viene riprodotto ... finché non viene utilizzato il modello.

  • + Il contenuto è considerato non presente nel documento. L'utilizzo di document.getElementById()o querySelector()nella pagina principale non restituirà i nodi figli di un modello.

  • + I modelli possono essere posizionati ovunque all'interno di <head>, <body>o <frameset>e possono contenere qualsiasi tipo di contenuto consentito in quegli elementi. Notare che "ovunque" significa che <template>può essere tranquillamente utilizzato in luoghi non consentiti dal parser HTML.

Ricaderci

Il supporto del browser non dovrebbe essere un problema, ma se vuoi coprire tutte le possibilità puoi fare un facile controllo:

Per rilevare la funzionalità <template>, crea l'elemento DOM e verifica che la proprietà .content esista:

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

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

Alcuni approfondimenti sul metodo di scripting di sovraccarico

  • + Non viene visualizzato nulla: il browser non esegue il rendering di questo blocco perché il <script>tag lo ha display:noneper impostazione predefinita.
  • + Inerte: il browser non analizza il contenuto dello script come JS perché il suo tipo è impostato su qualcosa di diverso da "text/javascript".
  • -Problemi di sicurezza: incoraggia l'uso di .innerHTML. L'analisi delle stringhe in fase di esecuzione dei dati forniti dagli utenti può facilmente portare a vulnerabilità XSS.

Articolo completo: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

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

CREAZIONE DI COMPONENTI WEB Creazione di tutorial su componenti Web personalizzati utilizzando modelli HTML di Trawersy Media: https://youtu.be/PCWaFLy3VUo


4
L' templateelemento non è supportato da Internet Explorer (a partire dal 2018, IE11). Testato con l'esempio 580 di w3c.github.io/html/single-page.html .
Roland

Mi piace <template>, anche se consiglierei di usare le classi liberamente, quindi è chiaro quali oggetti vengono impostati su cosa. Occorrono ancora alcune competenze js per mappare correttamente i dati nel modello, soprattutto utilizzando un approccio in stile componenti per la mappatura in grandi set di dati. Personalmente mi piace ancora costruire l'HTML in funzioni, o idealmente costruire il DOM direttamente con JS stesso e rinunciare del tutto all'HTML (ad esempio come lo fa React).
Josh da Qaribou,

L'approccio del modello ha funzionato bene per me. Una raccomandazione che ho è quella di clonare prima il modello (piuttosto che alla fine) e lavorare con l'oggetto clonato. Altrimenti, le modifiche che stai apportando stanno accadendo al modello stesso. Se avessi una logica condizionale in cui imposti alcune proprietà / contenuti solo per alcune volte, quelle proprietà sarebbero presenti nel tuo prossimo utilizzo del modello. Clonando prima di ogni utilizzo, ti assicuri di avere sempre una base coerente su cui lavorare.
jt.

13

Aggiungi da qualche parte nel corpo

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

quindi crea css

.hide { display: none; }

e aggiungi al tuo js

$('#output').append( $('.hide').html() );

1
L'OP ha un JSON e vuole eseguire il rendering di alcuni html usando quell'array come origine dati. In che modo questa risposta sta affrontando il problema? La tua risposta prende un nodo html e lo clona / duplica e nulla relativo al data binding.
Adrian Iftode

Quindi sono io che sto votando.
Adrian Iftode

Penso davvero che tu non stia aiutando a ottenere una risposta migliore, poiché avresti potuto iniziare ricordandoci che il punto era più ampio di ciò che è stato affrontato dalle nostre risposte.
Sebastian Neira

1
Questo funziona, ma è più efficiente da fare .children().clone(), piuttosto che .html(). La velocità è di circa 3: 1 per un random <tr/>che avevo su una pagina utilizzando questo codice i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. Il rapporto è in realtà un po 'più esagerato perché sto usando $ a.clone (), ma provare a svuotarlo ogni iterazione è più un successo delle prestazioni che la clonazione, quindi non sono sicuro di come renderlo più accurato perché le funzioni di temporizzazione hanno il loro costo.
Chinoto Vokro,

1
Questo approccio è negativo. principalmente perché ottieni il download del contenuto del modello e analizzato anche se non lo hai utilizzato. Questo è un problema troppo grande per considerarlo una risposta valida. Inoltre, come qualcun altro ha menzionato sopra, se devi almeno usare clone invece di .html ()
DevWL

3

Altra alternativa: Pure

Lo uso e mi ha aiutato molto. Un esempio mostrato sul loro sito web:

HTML

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

JSON

{
  "who": "Hello Wrrrld"
}

Risultato

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

2

Per risolvere questo problema, riconosco due soluzioni:

  • Il primo va con AJAX, con il quale dovrai caricare il modello da un altro file e aggiungerlo ogni volta che vuoi con .clone().

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

    Tieni presente che l'evento dovrebbe essere aggiunto una volta completato l'ajax per assicurarti che i dati siano disponibili!

  • Il secondo sarebbe aggiungerlo direttamente ovunque nell'html originale, selezionarlo e nasconderlo in jQuery:

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

    Puoi dopo aggiungere una nuova istanza del modello con

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • Come il precedente, ma se non vuoi che il template rimanga lì, ma solo in javascript, penso che puoi usare (non l'ho testato!) .detach()Invece di nascondere.

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

    .detach() rimuove gli elementi dal DOM mantenendo in vita i dati e gli eventi (.remove () no!).


-- Nessun problema! -
iConnor

L'operazione ha un JSON e vuole eseguire il rendering di un po 'di HTML usando quell'array come origine dati. In che modo questa risposta sta affrontando il problema? La tua risposta prende un nodo html e lo clona / duplica e nulla relativo al data binding.
Adrian Iftode

1
Cito da op: voglio aggiungere una copia del codice HTML seguente a un elemento contenitore con alcuni valori. Credo che questo risolva il suo problema.
Sebastian Neira

E come viene trattata la parte "alcuni valori"?
Adrian Iftode

Bene, questo non significa che non affronti il ​​problema. Apprezzo che tu mi abbia detto che mi mancava uno dei punti :) Comunque, come faccio a sapere come inserire quei valori se non so nemmeno di quali valori stiamo parlando? Dai un'occhiata a questa parte Dove posso inserire questo HTML per riutilizzarlo in modo intelligente? . Cosa si chiede veramente? Informazioni sull'inclusione di JSON o HTML?
Sebastian Neira

1

Ottima risposta da DevWL sull'utilizzo dell'elemento template HTML5 nativo . Per contribuire a questa buona domanda dall'OP vorrei aggiungere su come utilizzare questo templateelemento utilizzando jQuery, ad esempio:

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

Il contenuto di templatenon è HTML, ma viene trattato come dati, quindi è necessario avvolgere il contenuto in un oggetto jQuery per accedere ai metodi di jQuery.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.