Définition d'un modèle HTML à ajouter à l'aide de JQuery

125

J'ai un tableau sur lequel je boucle. Chaque fois qu'une condition est vraie, je souhaite ajouter une copie du HTMLcode ci-dessous à un élément conteneur avec certaines valeurs.

Où puis-je réutiliser ce code HTML de manière 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
    });
});
Patrick Reck
la source
2
Si vous cherchez un moyen intelligent, mettez toutes vos informations dans un seul DIV et stylisez-le. Ne créez pas de nombreux tableaux avec seulement deux cellules, ne les enroulez pas dans des ancres.
Sergey Snegirev
3
OP est évidemment intéressé par les bonnes pratiques de codage (bravo!), Alors j'ai voulu aider. Si je voulais contribuer au problème réel, je posterais une réponse. Je suis sûr que tout le monde convient que l'utilisation d'un tableau à deux cellules pour positionner une image et du texte est à peine justifiée, sauf pour des exigences vraiment exotiques (IE4?)
Sergey Snegirev
1
@BrianG. Publier un commentaire n'implique pas vraiment que vous partez sur une tangente. Bien que je convienne que les commentaires sont un lieu approprié pour ceux-ci (tant qu'ils sont toujours pertinents), ils sont également appropriés pour les conseils au volant de personnes qui n'ont pas encore le temps de les développer dans une réponse. Il est utile au PO de préciser ce que vous faites.
millimoose
Personne n'a répondu à ta question, Patrick?
Sebastian Neira

Réponses:

164

Vous pouvez décider d'utiliser un moteur de création de modèles dans votre projet, tel que:

Si vous ne souhaitez pas inclure une autre bibliothèque, John Resig propose une solution jQuery , similaire à celle ci-dessous.


Les navigateurs et lecteurs d'écran ignorent les types de scripts non reconnus:

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

En utilisant jQuery, l'ajout de lignes basées sur le modèle ressemblerait à:

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

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});
Mathijs Flietstra
la source
@MaksimLuzik a raison. Moustache (notez l'orthographe de la marque) est plus léger et se connectera au code de l'OP, mais Handlebars a plus de fonctionnalités pour gérer les tableaux et peut fournir une solution plus suffisante à la fin. Voici un bel article de comparaison: blog.cubettech.com
Michael Scheper
13
Exemple de modification du modèle ici: jsfiddle.net/meehanman/7vw8bc84
Dean Meehan
175

Ancienne question, mais puisque la question demande "en utilisant jQuery", j'ai pensé fournir une option qui vous permet de faire cela sans introduire de dépendance au fournisseur.

Bien qu'il existe de nombreux moteurs de création de modèles, nombre de leurs fonctionnalités ont récemment été défavorisées, avec l'itération ( <% for), les conditions ( <% if) et les transformations (<%= myString | uppercase %> ) considérées au mieux comme du microlangage, et au pire des anti-modèles. Les pratiques de création de modèles modernes encouragent simplement le mappage d'un objet à sa représentation DOM (ou autre), par exemple ce que nous voyons avec les propriétés mappées aux composants dans ReactJS (en particulier les composants sans état).

Modèles dans HTML

Une propriété sur laquelle vous pouvez compter pour conserver le HTML de votre modèle à côté du reste de votre HTML consiste à utiliser un fichier non exécutable <script> type, par exemple <script type="text/template">. Pour votre cas:

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

Lors du chargement du document, lisez votre modèle et jetez-le en utilisant un simple String#split

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

Notez qu'avec notre token, vous l'obtenez au [text, property, text, property]format alterné . Cela nous permet de le mapper joliment en utilisant un Array#map, avec une fonction de mappage:

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

propspourrait ressembler { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

En supposant que vous avez analysé et chargé votre itemTplcomme ci-dessus, et que vous avez un itemstableau dans la portée:

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

Cette approche est également à peine jQuery - vous devriez être en mesure d'adopter la même approche en utilisant javascript vanilla avec document.querySelectoret .innerHTML.

jsfiddle

Modèles dans JS

Une question à vous poser est: voulez-vous vraiment / avez-vous vraiment besoin de définir des modèles comme des fichiers HTML? Vous pouvez toujours composant + réutiliser un modèle de la même manière que vous réutiliseriez la plupart des choses que vous souhaitez répéter: avec une fonction.

Dans es7-land, en utilisant la déstructuration, les chaînes de modèle et les fonctions fléchées, vous pouvez écrire de jolies fonctions de composant qui peuvent être facilement chargées en utilisant la $.fn.htmlméthode ci-dessus.

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

Ensuite, vous pouvez facilement le rendre, même mappé à partir d'un tableau, comme ceci:

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

Oh et note finale: n'oubliez pas de nettoyer vos propriétés passées à un modèle, si elles sont lues à partir d'une base de données, ou quelqu'un pourrait passer du HTML (puis exécuter des scripts, etc.) depuis votre page.

Josh de Qaribou
la source
1
Beau moteur de modèle.
Arthur Castro
41
Vos modèles dans la section JS sont la bombe
BigRon
2
Pourriez-vous s'il vous plaît ajouter une démo / violon pour l'approche "Modèles dans HTML"?
LCJ
1
Hou la la! Modèle à l'intérieur de JS ... Jamais pensé à ça! Incroyable!
Roman Lopez
2
Wow, c'est vraiment très sympa. J'ajoute un attribut data-url à chaque modèle. Et récupérer les données ajax pour chacun. C'est tellement gentil et cela m'a fait gagner beaucoup de temps! Je vous remercie.
Davey
42

Utilisez plutôt un modèle HTML!

Étant donné que la réponse acceptée représenterait une méthode de script de surcharge, je voudrais en suggérer une autre qui est, à mon avis, beaucoup plus propre et plus sûre en raison des risques XSS qui viennent avec la surcharge des scripts.

J'ai fait une démo pour vous montrer comment l'utiliser dans une action et comment injecter un modèle dans un autre, éditer puis ajouter au document DOM.

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

exemple 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 <modèle>

  • + Son contenu est effectivement inerte jusqu'à son activation. Essentiellement, votre balisage est masqué DOM et ne rend pas.

  • + Tout contenu dans un modèle n'aura pas d'effets secondaires. Les scripts ne s'exécutent pas, les images ne se chargent pas, l'audio ne joue pas ... tant que le modèle n'est pas utilisé.

  • + Le contenu est considéré comme ne faisant pas partie du document. L'utilisation de document.getElementById()ou querySelector()dans la page principale ne renvoie pas les nœuds enfants d'un modèle.

  • + Les modèles peuvent être placés n'importe où à l'intérieur de <head>, <body>ou <frameset>et peuvent contenir tout type de contenu autorisé dans ces éléments. Notez que «n'importe où» signifie qu'il <template>peut être utilisé en toute sécurité dans des endroits que l'analyseur HTML interdit.

Se retirer

La prise en charge du navigateur ne devrait pas être un problème, mais si vous souhaitez couvrir toutes les possibilités, vous pouvez effectuer une vérification facile:

Pour détecter les fonctionnalités <template>, créez l'élément DOM et vérifiez que la propriété .content existe:

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

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

Quelques informations sur la méthode de script de surcharge

  • + Rien n'est rendu - le navigateur ne rend pas ce bloc car la <script>balise l'a display:nonepar défaut.
  • + Inert - le navigateur n'analyse pas le contenu du script en tant que JS car son type est défini sur autre chose que "text/javascript".
  • -Problèmes de sécurité - encourage l'utilisation de .innerHTML. L'analyse des chaînes d'exécution des données fournies par l'utilisateur peut facilement conduire à des vulnérabilités XSS.

Article complet: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

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

CRÉATION DE COMPOSANTS WEB Tutoriel sur la création de composants Web personnalisés à l'aide de modèles HTML de Trawersy Media: https://youtu.be/PCWaFLy3VUo

DevWL
la source
4
L' templateélément n'est pas pris en charge par Internet Explorer (à partir de 2018, IE11). Testé avec l'exemple 580 de w3c.github.io/html/single-page.html .
Roland
J'aime le <template>, bien que je recommande d'utiliser les classes libéralement, donc il est clair quels accessoires sont définis sur quoi. Il faut encore quelques compétences js pour mapper correctement vos données dans le modèle, en particulier en utilisant une approche de type composants pour le mappage dans de grands ensembles de données. Personnellement, j'aime toujours simplement construire le HTML dans les fonctions, ou idéalement construire le DOM directement avec JS lui-même et renoncer complètement au HTML (par exemple, comment React le fait).
Josh de Qaribou le
L'approche du modèle a bien fonctionné pour moi. Une recommandation que j'ai est de cloner d'abord le modèle (plutôt qu'à la fin) et de travailler avec l'objet cloné. Sinon, les modifications que vous apportez concernent le modèle lui-même. Si vous aviez une logique conditionnelle dans laquelle vous ne définissiez certaines propriétés / contenus que de temps en temps, ces propriétés seraient présentes lors de votre prochaine utilisation du modèle. En clonant avant chaque utilisation, vous vous assurez de toujours disposer d'une base cohérente sur laquelle travailler.
jt.
13

Ajouter quelque part dans le corps

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

puis créez css

.hide { display: none; }

et ajoutez à vos js

$('#output').append( $('.hide').html() );
Mateusz Nowak
la source
1
L'OP a un JSON et veut rendre du HTML en utilisant ce tableau comme source de données. Comment cette réponse résout le problème? Votre réponse prend un nœud html et le clone / duplique et rien lié à la liaison de données.
Adrian Iftode
C'est donc moi qui vote contre.
Adrian Iftode
Je pense vraiment que vous n'aidez pas à obtenir une meilleure réponse, car vous auriez pu commencer par nous rappeler que le point était plus large que ce qui était abordé par nos réponses.
Sebastian Neira
1
Cela fonctionne, mais il est plus efficace de le faire .children().clone()que de le faire .html(). La vitesse est d'environ 3: 1 pour un aléatoire <tr/>que j'avais sur une page en utilisant ce code i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. Le rapport est en fait un peu plus exagéré car j'utilise $ a.clone (), mais essayer de le vider à chaque itération est plus un problème de performance que le clonage, donc je ne sais pas comment le rendre plus précis car les fonctions de chronométrage ont leur propre coût.
Chinoto Vokro
1
Cette approche est mauvaise. principalement parce que vous téléchargez et analysez le contenu du modèle même si vous ne l'avez pas utilisé. C'est trop gros problème pour considérer cela comme une réponse valable. Aussi comme quelqu'un d'autre l'a mentionné ci-dessus, si vous devez au moins utiliser clone au lieu de .html ()
DevWL
3

Autre alternative: Pure

Je l'utilise et cela m'a beaucoup aidé. Un exemple montré sur leur site Web:

HTML

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

JSON

{
  "who": "Hello Wrrrld"
}

Résultat

<div class="who">
  Hello Wrrrld
</div>
aldite
la source
2

Afin de résoudre ce problème, je reconnais deux solutions:

  • Le premier va avec AJAX, avec lequel vous devrez charger le modèle à partir d'un autre fichier et l'ajouter à chaque fois que vous le souhaitez .clone().

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

    Tenez compte du fait que l'événement doit être ajouté une fois que l'ajax est terminé pour être sûr que les données sont disponibles!

  • Le second serait de l'ajouter directement n'importe où dans le html d'origine, de le sélectionner et de le cacher dans jQuery:

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

    Vous pouvez ensuite ajouter une nouvelle instance du modèle avec

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • Identique au précédent, mais si vous ne voulez pas que le template reste là, mais juste dans le javascript, je pense que vous pouvez utiliser (ne l'avez pas testé!) Au .detach()lieu de hide.

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

    .detach() supprime les éléments du DOM tout en gardant les données et les événements en vie (.remove () ne le fait pas!).

Sebastian Neira
la source
-- Pas de problème! -
iConnor
L'op a un JSON et souhaite rendre du HTML en utilisant ce tableau comme source de données. Comment cette réponse résout le problème? Votre réponse prend un nœud html et le clone / duplique et rien lié à la liaison de données.
Adrian Iftode
1
Je cite op: Je souhaite ajouter une copie du code HTML ci-dessous à un élément conteneur avec certaines valeurs. Je pense que cela résout son problème.
Sebastian Neira
Et comment est traitée la partie «quelques valeurs»?
Adrian Iftode
Eh bien, cela ne signifie pas que cela ne résout pas le problème. J'apprécie que vous m'ayez dit qu'il me manquait un des points :) Bref, comment savoir comment insérer ces valeurs si je ne sais même pas de quelles valeurs parlons-nous? Jetez un œil à cette partie Où puis-je réutiliser ce code HTML de manière intelligente? . Que demande-t-on vraiment? À propos du JSON ou de l'inclusion HTML?
Sebastian Neira
1

Très bonne réponse de DevWL sur l'utilisation de l' élément de modèle HTML5 natif . Pour contribuer à cette bonne question de l'OP, je voudrais ajouter comment utiliser cet templateélément en utilisant jQuery, par exemple:

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

Le contenu de templaten'est pas html, mais simplement traité comme des données, vous devez donc envelopper le contenu dans un objet jQuery pour accéder aux méthodes de jQuery.

Jacques
la source