Definición de una plantilla HTML para agregar usando JQuery

125

Tengo una matriz que estoy recorriendo. Cada vez que una condición es verdadera, quiero adjuntar una copia delHTML código a continuación a un elemento contenedor con algunos valores.

¿Dónde puedo poner este HTML para reutilizarlo de forma inteligente?

<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
fuente
2
Si está buscando una forma inteligente, ponga toda su información en un solo DIV y ajústelo. No cree numerosas tablas con solo dos celdas, no las envuelva en anclajes.
Sergey Snegirev
3
OP está obviamente interesado en las prácticas de codificación adecuadas (¡felicitaciones!), Así que quería ayudar. Si quisiera contribuir al problema real, publicaría una respuesta. Estoy seguro de que alguien está de acuerdo en que el uso de una tabla completa de dos celdas para colocar una imagen y algo de texto apenas se justifica, excepto por algunos requisitos realmente exóticos (¿IE4?)
Sergey Snegirev
1
@BrianG. Sin embargo, publicar un comentario no implica que te vayas por la tangente. Si bien estoy de acuerdo en que los comentarios son un lugar apropiado para esos (siempre que sigan siendo relevantes), también son apropiados para las sugerencias de personas que aún no tienen tiempo para expandirlas en una respuesta. Es útil para el OP dejar en claro lo que está haciendo.
millimoose
¿Nadie respondió a tu pregunta, Patrick?
Sebastian Neira

Respuestas:

164

Puede decidir hacer uso de un motor de plantillas en su proyecto, como:

Si no desea incluir otra biblioteca, John Resig ofrece una solución jQuery , similar a la que se muestra a continuación.


Los navegadores y lectores de pantalla ignoran los tipos de secuencia de comandos no reconocidos:

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

Usando jQuery, agregar filas basadas en la plantilla se parecería a:

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

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});
Mathijs Flietstra
fuente
@MaksimLuzik tiene razón. Moustache (tenga en cuenta la ortografía de la marca registrada) es más liviano y se conectará al código del OP, pero Handlebars tiene más funciones para manejar matrices y puede proporcionar una solución más suficiente al final. Aquí hay un buen artículo comparativo: blog.cubettech.com/…
Michael Scheper
13
Ejemplo de cómo editar la plantilla aquí: jsfiddle.net/meehanman/7vw8bc84
Dean Meehan
175

Pregunta antigua, pero como la pregunta es "usar jQuery", pensé en proporcionar una opción que le permita hacer esto sin introducir ninguna dependencia del proveedor.

Si bien hay muchos motores de plantillas, muchas de sus características han caído en desuso recientemente, con iteración ( <% for), condicionales ( <% if) y transformaciones (<%= myString | uppercase %> ) vistos como microlenguaje en el mejor de los casos y como antipatrones en el peor. Las prácticas modernas de creación de plantillas fomentan la simple asignación de un objeto a su representación DOM (u otra), por ejemplo, lo que vemos con las propiedades asignadas a los componentes en ReactJS (especialmente los componentes sin estado).

Plantillas dentro de HTML

Una propiedad en la que puede confiar para mantener el HTML de su plantilla junto al resto de su HTML es mediante el uso de un código no ejecutable <script> type, por ejemplo <script type="text/template">. Para tu 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 cargar el documento, lea su plantilla y conviértala en token con un simple String#split

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

Tenga en cuenta que con nuestro token, lo obtiene en [text, property, text, property]formato alterno . Esto nos permite mapearlo muy bien usando an Array#map, con una función de mapeo:

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

Dónde propspodría verse { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Poniéndolo todo junto asumiendo que has analizado y cargado tu itemTplcomo arriba, y tienes una itemsmatriz dentro del alcance:

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

Este enfoque también es apenas jQuery: debería poder adoptar el mismo enfoque usando javascript vanilla con document.querySelectory .innerHTML.

jsfiddle

Plantillas dentro de JS

Una pregunta que debe hacerse es: ¿realmente desea / necesita definir plantillas como archivos HTML? Siempre puede componenteizar + reutilizar una plantilla de la misma manera que reutilizaría la mayoría de las cosas que desea repetir: con una función.

En es7-land, usando desestructuración, cadenas de plantilla y funciones de flecha, puede escribir funciones de componentes de aspecto francamente bonito que se pueden cargar fácilmente usando el $.fn.htmlmétodo anterior.

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

Entonces podría renderizarlo fácilmente, incluso mapeado desde una matriz, así:

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

Ah, y nota final: no se olvide de desinfectar sus propiedades pasadas a una plantilla, si se leen desde una base de datos, o si alguien podría pasar HTML (y luego ejecutar scripts, etc.) desde su página.

Josh de Qaribou
fuente
1
Buen motor de plantillas.
Arthur Castro
41
Tus plantillas dentro de la sección JS son la bomba
BigRon
2
¿Podría agregar una demostración / violín para el enfoque "Plantillas dentro de HTML"?
LCJ
1
¡Guauu! Plantilla dentro de JS ... ¡Nunca pensé en eso! ¡Asombroso!
Roman Lopez
2
Vaya, esto es realmente muy bueno. Estoy agregando un atributo data-url a cada plantilla. Y obteniendo datos ajax para cada uno. ¡Esto es tan lindo y me ahorró mucho tiempo! Gracias.
Davey
42

¡Utilice plantilla HTML en su lugar!

Dado que la respuesta aceptada representaría un método de script de sobrecarga, me gustaría sugerir otro que, en mi opinión, es mucho más limpio y seguro debido a los riesgos de XSS que vienen con los scripts de sobrecarga.

Hice una demostración para mostrarte cómo usarlo en una acción y cómo inyectar una plantilla en otra, editar y luego agregar al documento DOM.

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

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

  • + Su contenido es efectivamente inerte hasta que se activa. Esencialmente, su marcado es DOM oculto y no se procesa.

  • + Cualquier contenido dentro de una plantilla no tendrá efectos secundarios. Los scripts no se ejecutan, las imágenes no se cargan, el audio no se reproduce ... hasta que se usa la plantilla.

  • + Se considera que el contenido no está en el documento. El uso de document.getElementById()o querySelector()en la página principal no devolverá los nodos secundarios de una plantilla.

  • + Las plantillas se pueden colocar en cualquier lugar dentro de <head>, <body>o <frameset>y pueden contener cualquier tipo de contenido que esté permitido en esos elementos. Tenga en cuenta que "en cualquier lugar" significa que <template>se puede utilizar de forma segura en lugares que el analizador HTML no permite.

Retroceder

La compatibilidad con el navegador no debería ser un problema, pero si desea cubrir todas las posibilidades, puede realizar una verificación sencilla:

Para la detección de características <template>, cree el elemento DOM y verifique que exista la propiedad .content:

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

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

Algunas ideas sobre el método de script de sobrecarga

  • + No se procesa nada: el navegador no procesa este bloque porque la <script>etiqueta tiene display:nonede forma predeterminada.
  • + Inerte: el navegador no analiza el contenido de la secuencia de comandos como JS porque su tipo está configurado en algo diferente a "text/javascript".
  • -Problemas de seguridad: fomenta el uso de .innerHTML. El análisis de cadenas en tiempo de ejecución de los datos proporcionados por el usuario puede conducir fácilmente a vulnerabilidades XSS.

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

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

CREACIÓN DE COMPONENTES WEB Tutorial de creación de componentes web personalizados utilizando plantillas HTML de Trawersy Media: https://youtu.be/PCWaFLy3VUo

DevWL
fuente
4
El templateelemento no es compatible con Internet Explorer (a partir de 2018, IE11). Probado con el ejemplo 580 de w3c.github.io/html/single-page.html .
Roland
Me gusta <template>, aunque recomendaría usar las clases generosamente, por lo que está claro qué accesorios se están configurando para qué. Todavía se necesitan algunas habilidades de js para mapear sus datos en la plantilla correctamente, especialmente usando un enfoque de estilo de componentes para mapear grandes conjuntos de datos. Personalmente, todavía me gusta simplemente construir el HTML en funciones, o idealmente construir el DOM directamente con JS y renunciar al HTML por completo (por ejemplo, cómo lo hace React).
Josh de Qaribou
El enfoque de la plantilla funcionó bien para mí. Una recomendación que tengo es clonar la plantilla primero (en lugar de al final) y trabajar con el objeto clonado. De lo contrario, los cambios que realice se producirán en la propia plantilla. Si tuviera una lógica condicional en la que estableciera algunas propiedades / contenido solo algunas veces, esas propiedades estarían presentes en su próximo uso de la plantilla. Al clonar antes de cada uso, se asegura de tener siempre una base coherente desde la que trabajar.
jt.
13

Agregar en algún lugar del cuerpo

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

luego crea css

.hide { display: none; }

y agrega a tu js

$('#output').append( $('.hide').html() );
Mateusz Nowak
fuente
1
El OP tiene un JSON y quiere renderizar algo de html usando esa matriz como fuente de datos. ¿Cómo esta respuesta aborda el problema? Su respuesta toma un nodo html y lo clona / duplica y nada relacionado con el enlace de datos.
Adrian Iftode
Así que soy yo quien está votando negativamente.
Adrian Iftode
Realmente creo que no está ayudando a obtener una mejor respuesta, ya que podría haber comenzado recordándonos que el punto era más amplio que lo que se aborda en nuestras respuestas.
Sebastian Neira
1
Esto funciona, pero es más eficiente hacerlo en .children().clone()lugar de hacerlo .html(). La velocidad es de aproximadamente 3: 1 para un aleatorio <tr/>que tuve en una página usando este código i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. La proporción es en realidad un poco más exagerada porque estoy usando $ a.clone (), pero intentar vaciarlo en cada iteración es más un impacto en el rendimiento que la clonación, por lo que no estoy seguro de cómo hacerlo más preciso porque las funciones de cronometraje tienen su propio costo.
Chinoto Vokro
1
Este enfoque es malo. principalmente porque obtiene el contenido de la plantilla descargado y analizado incluso si no lo usó. Este es un problema demasiado grande para considerarlo como una respuesta válida. También como alguien más mencionó anteriormente, si debe al menos usar clonar en lugar de .html ()
DevWL
3

Otra alternativa: pura

Lo uso y me ha ayudado mucho. Un ejemplo que se muestra en su sitio web:

HTML

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

JSON

{
  "who": "Hello Wrrrld"
}

Resultado

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

Para resolver este problema, reconozco dos soluciones:

  • El primero va con AJAX, con el que tendrás que cargar la plantilla desde otro archivo y solo agregar cada vez que quieras .clone().

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

    ¡Tenga en cuenta que el evento debe agregarse una vez que se haya completado el ajax para asegurarse de que los datos estén disponibles!

  • El segundo sería agregarlo directamente en cualquier lugar del html original, seleccionarlo y ocultarlo en jQuery:

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

    Después puede agregar una nueva instancia de la plantilla con

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • Igual que el anterior, pero si no desea que la plantilla permanezca allí, pero solo en el javascript, creo que puede usar (¡no lo he probado!) En .detach()lugar de ocultar.

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

    .detach() elimina elementos del DOM mientras mantiene vivos los datos y eventos (.remove () no lo hace).

Sebastián Neira
fuente
-- ¡No hay problema! -
iConnor
La operación tiene un JSON y quiere renderizar algo de html usando esa matriz como fuente de datos. ¿Cómo esta respuesta aborda el problema? Su respuesta toma un nodo html y lo clona / duplica y nada relacionado con el enlace de datos.
Adrian Iftode
1
Cito de op: quiero agregar una copia del código HTML a continuación a un elemento contenedor con algunos valores. Creo que esto soluciona su problema.
Sebastian Neira
¿Y cómo se trata la parte de "algunos valores"?
Adrian Iftode
Bueno, eso no significa que no aborde el problema. Aprecio que me hayas dicho que me faltaba uno de los puntos :) De todos modos, ¿cómo voy a saber cómo insertar esos valores si ni siquiera sé de qué valores estamos hablando? Solo eche un vistazo a esta parte ¿Dónde puedo poner este HTML para reutilizarlo de una manera inteligente? . ¿Qué se pregunta realmente? ¿Sobre JSON o la inclusión de HTML?
Sebastian Neira
1

Muy buena respuesta de DevWL sobre el uso del elemento de plantilla HTML5 nativo . Para contribuir a esta buena pregunta del OP, me gustaría agregar cómo usar este templateelemento usando jQuery, por ejemplo:

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

El contenido templateno es html, sino que solo se trata como datos, por lo que debe envolver el contenido en un objeto jQuery para luego acceder a los métodos de jQuery.

Jacques
fuente