Al generar un avance o resumen de contenido HTML, muchas personas simplemente eliminan todas las etiquetas antes de seleccionar la n más a la izquierda. caracteres. Recientemente, en el blog del desarrollador de ColdFusion, Ben Nadel, abordó el problema de cerrar etiquetas XHTML en una cadena truncada usando ColdFusion y sus métodos subyacentes de Java. Después de ver esto, creé una versión de JavaScript más o menos equivalente y agregué algunas funciones adicionales. Específicamente, el siguiente código trunca adicionalmente la cadena (según un número de caracteres especificado por el usuario), y en el proceso solo cuenta el texto fuera de las etiquetas HTML hacia la longitud, evita terminar la cadena en medio de una etiqueta o palabra y evita agregar etiquetas de cierre para elementos únicos como <br>
o <img>
.
function getLeadingHtml (input, maxChars) { // token matches a word, tag, or special character var token = /\w+|[^\w<]|<(\/)?(\w+)[^>]*(\/)?>|</g, selfClosingTag = /^(?:[hb]r|img)$/i, output = "", charCount = 0, openTags = [], match; // Set the default for the max number of characters // (only counts characters outside of HTML tags) maxChars = maxChars || 250; while ((charCount < maxChars) && (match = token.exec(input))) { // If this is an HTML tag if (match[2]) { output += match[0]; // If this is not a self-closing tag if (!(match[3] || selfClosingTag.test(match[2]))) { // If this is a closing tag if (match[1]) openTags.pop(); else openTags.push(match[2]); } } else { charCount += match[0].length; if (charCount <= maxChars) output += match[0]; } } // Close any tags which were left open var i = openTags.length; while (i--) output += "</" + openTags[i] + ">"; return output; };
Todo esto es bastante sencillo, pero pensé que también podría transmitirlo.
Aquí hay un ejemplo de la salida:
var input = '<p><a href="http://www.realultimatepower.net/">Ninjas</a> are mammals<br>who <strong><em>love</em> to <u>flip out and cut off people\'s heads all the time!</u></strong></p>'; var output = getLeadingHtml(input, 40); /* Output: <p><a href="http://www.realultimatepower.net/">Ninjas</a> are mammals<br>who <strong><em>love</em> to <u>flip out </u></strong></p> */
Editar: En una nota relacionada, aquí hay una expresión regular que publiqué anteriormente en el sitio de Ben que coincide con los primeros 100 caracteres de una cadena, a menos que termine en medio de una etiqueta HTML, en cuyo caso coincidirá hasta el final de la etiqueta (utilice este con el modificador "punto coincide con nueva línea"):
^.{1,100}(?:(?<=<[^>]{0,99})[^>]*>)?
Eso debería funcionar con los motores .NET, Java y JGsoft regex. No funcionará en la mayoría de los demás debido al {0,99}
en la mirada atrás. Tenga en cuenta que .NET y JGsoft en realidad admiten la búsqueda de longitud infinita, por lo que con esos dos podría reemplazar el {0,99}
cuantificador con *
. Dado que los motores .NET y JGsoft también admiten condicionales basados en la búsqueda, puede guardar dos caracteres más escribiéndolos como ^.{1,100}(?(?<=<[^>]{0,99})[^>]*>)
.
Si desea imitar el lookbehind en JavaScript, puede usar lo siguiente:
// JavaScript doesn't include a native reverse method for strings, // so we need to create one String.prototype.reverse = function() { return this.split("").reverse().join(""); }; // Mimic the regex /^[\S\s]{1,100}(?:(?<=<[^>]*)[^>]*>)?/ through // node-by-node reversal var regex = /(?:>[^>]*(?=[^>]*<))?[\S\s]{1,100}$/; var output = input.reverse().match(regex)[0].reverse();