2010-09-13 14 views
159

tengo cadenas comoDecode & volver a & en JavaScript

var str = 'One & two & three'; 

dictada en HTML por el servidor web. Me necesidad de transformar esas cadenas en

'One & two & three' 

Actualmente, eso es lo que estoy haciendo (con ayuda de jQuery):

$(document.createElement('div')).html('{{ driver.person.name }}').text() 

sin embargo tengo una sensación inquietante de que estoy haciendo mal. He tratado

unescape("&") 

pero no parecen funcionar, tampoco decodeURI/decodeURIComponent.

¿Hay alguna otra manera más nativa y elegante de hacerlo?

+1

como cadenas que contienen entidades HTML son algo diferente a [ 'escape'] (https: //developer.moz illa.org/en/DOM/window.escape)d o [cadenas codificadas URI] (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent), esas funciones no funcionarán. –

+0

La gran función incluida en este artículo parece funcionar bien: http://blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx No creo que sea la solución más inteligente, pero funciona . – Matias

+1

@Matias tenga en cuenta que las nuevas entidades con nombre se han agregado a HTML (por ejemplo, a través de la especificación HTML 5) ya que esa función se creó en 2003, por ejemplo, no reconoce '𝕫'. Este es un problema con una especificación en evolución; como tal, debe elegir una herramienta que en realidad se mantiene para resolverlo. –

Respuesta

31

Una opción más moderna para interpretar HTML (texto y de otro modo) desde JavaScript es el soporte HTML en la API DOMParser ()). Esto le permite usar el analizador HTML nativo del navegador para convertir una cadena en un documento HTML. Se ha admitido en las nuevas versiones de todos los principales navegadores desde finales de 2014.

Si solo queremos decodificar un poco de contenido de texto, podemos ponerlo como único contenido en un cuerpo de documento, analizar el documento y extraer el es .body.textContent.

var encodedStr = 'hello & world'; 
 

 
var parser = new DOMParser; 
 
var dom = parser.parseFromString(
 
    '<!doctype html><body>' + encodedStr, 
 
    'text/html'); 
 
var decodedString = dom.body.textContent; 
 

 
console.log(decodedString);

Podemos ver en the draft specification for DOMParser que JavaScript no está habilitado para el documento analizado, por lo que podemos realizar esta conversión texto sin problemas de seguridad.

El método parseFromString(str, type) debe ejecutar estos pasos, dependiendo del tipo :

  • "text/html"

    Analizar str con un HTML parser, y devolver el Document recién creado.

    El indicador de scripting debe establecerse en "deshabilitado".

    NOTA

    script elementos son marcados inejecutables y el contenido de noscript consiguen analizados como marcado.

Está más allá del alcance de esta pregunta, pero tenga en cuenta que si usted está tomando el DOM analizado mismos nodos (no sólo su contenido de texto) y moverlos al documento DOM en directo , es posible que sus secuencias de comandos se vuelvan a activar, y podría haber problemas de seguridad. No lo he investigado, así que por favor tenga cuidado.

+0

cualquier alternativa para NodeJs? – anunixercoder

225

¿Necesita decodificar todas las entidades HTML codificadas o solo &amp;?

Si sólo necesita para manejar &amp; entonces usted puede hacer esto:

var decoded = encoded.replace(/&amp;/g, '&'); 

Si necesita decodificar todas las entidades HTML, entonces puede hacerlo sin jQuery:

var elem = document.createElement('textarea'); 
elem.innerHTML = encoded; 
var decoded = elem.value; 

Por favor, tome nota de los comentarios de Mark a continuación que resaltan los agujeros de seguridad en una versión anterior de esta respuesta y recomiendan usar textarea en lugar de div para mitigar las posibles vulnerabilidades de XSS. Estas vulnerabilidades existen ya sea que use jQuery o JavaScript simple.

+11

¡Ten cuidado! Esto es potencialmente inseguro. Si 'codificado = '' ', el fragmento de arriba mostrará una alerta. Esto significa que si el texto codificado proviene de la entrada del usuario, decodificarlo con este fragmento puede presentar una vulnerabilidad XSS. –

+0

@MarkAmery No soy un experto en seguridad, pero parece que si configuras el div en 'null' inmediatamente después de recibir el texto, la alerta en el img no se activa - http://jsfiddle.net/Mottie/gaBeb/ 128/ – Mottie

+3

@Mottie, tenga en cuenta el navegador que funcionó para usted, pero 'alert (1)' todavía se dispara para mí en Chrome en OS X. Si desea una variante segura de este truco, intente [usando un 'textarea' ] (http://stackoverflow.com/a/31350391/1709587). –

12

element.innerText también hace el truco.

4

En primer lugar crear un algún lugar <span id="decodeIt" style="display:none;"></span> en el cuerpo

A continuación, asignar la cadena a ser decodificada como innerHTML para esto:

document.getElementById("decodeIt").innerHTML=stringtodecode 

Por último,

stringtodecode=document.getElementById("decodeIt").innerText 

Aquí está la general código:

var stringtodecode="<B>Hello</B> world<br>"; 
document.getElementById("decodeIt").innerHTML=stringtodecode; 
stringtodecode=document.getElementById("decodeIt").innerText 
+0

-1; esto es peligrosamente inseguro para usar en entradas no confiables. Por ejemplo, considere lo que sucede si 'stringtodecode' contiene algo como' '. –

23
var htmlEnDeCode = (function() { 
    var charToEntityRegex, 
     entityToCharRegex, 
     charToEntity, 
     entityToChar; 

    function resetCharacterEntities() { 
     charToEntity = {}; 
     entityToChar = {}; 
     // add the default set 
     addCharacterEntities({ 
      '&amp;'  : '&', 
      '&gt;'  : '>', 
      '&lt;'  : '<', 
      '&quot;' : '"', 
      '&#39;'  : "'" 
     }); 
    } 

    function addCharacterEntities(newEntities) { 
     var charKeys = [], 
      entityKeys = [], 
      key, echar; 
     for (key in newEntities) { 
      echar = newEntities[key]; 
      entityToChar[key] = echar; 
      charToEntity[echar] = key; 
      charKeys.push(echar); 
      entityKeys.push(key); 
     } 
     charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g'); 
     entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g'); 
    } 

    function htmlEncode(value){ 
     var htmlEncodeReplaceFn = function(match, capture) { 
      return charToEntity[capture]; 
     }; 

     return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn); 
    } 

    function htmlDecode(value) { 
     var htmlDecodeReplaceFn = function(match, capture) { 
      return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10)); 
     }; 

     return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn); 
    } 

    resetCharacterEntities(); 

    return { 
     htmlEncode: htmlEncode, 
     htmlDecode: htmlDecode 
    }; 
})(); 

Esto es del código fuente ExtJS.

+0

-1; esto no puede manejar la gran mayoría de las entidades nombradas. Por ejemplo, 'htmlEnDecode.htmlDecode ('€')' debe devolver ''€'', pero en cambio devuelve ''€''. –

25

Matthias Bynens tiene una biblioteca para esto: https://github.com/mathiasbynens/he

Ejemplo:

console.log(
    he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ") 
); 
// Logs "Jörg & Jürgen rocked to & fro" 

Sugiero que favorece sobre hacks que implican el establecimiento de contenido HTML de un elemento y luego volver a leer su contenido de texto. Dichos enfoques pueden funcionar, pero son engañosamente peligrosos y presentan oportunidades de XSS si se usan en las entradas de usuarios que no son de confianza.

Si realmente no puede soportar cargar en una biblioteca, puede usar el truco textarea descrito en this answer en una pregunta casi duplicada, que, a diferencia de varios enfoques similares que se han sugerido, no tiene agujeros de seguridad que yo saber de:

function decodeEntities(encodedString) { 
    var textArea = document.createElement('textarea'); 
    textArea.innerHTML = encodedString; 
    return textArea.value; 
} 

console.log(decodeEntities('1 &amp; 2')); // '1 & 2' 

Pero tomar nota de los problemas de seguridad, que afecta a enfoques similares a éste, que enumero en la respuesta relacionado! Este enfoque es un truco, y los cambios futuros al contenido permitido de un textarea (o errores en navegadores específicos) podrían llevar a un código que dependa de que repentinamente tenga un agujero XSS un día.

+0

¡La biblioteca de Matthias Bynens 'él' es absolutamente genial! ¡Muchas gracias por la recomendación! – Hamsterrific

3

jQuery codificará y decodificará para usted. Sin embargo, debe usar una etiqueta textarea, no un div.

var str1 = 'One & two & three'; 
 
var str2 = "One &amp; two &amp; three"; 
 
    
 
$(document).ready(function() { 
 
    $("#encoded").text(htmlEncode(str1)); 
 
    $("#decoded").text(htmlDecode(str2)); 
 
}); 
 

 
function htmlDecode(value) { 
 
    return $("<textarea/>").html(value).text(); 
 
} 
 

 
function htmlEncode(value) { 
 
    return $('<textarea/>').text(value).html(); 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 
 

 
<div id="encoded"></div> 
 
<div id="decoded"></div>

+1

-1 porque hay un (sorprendente) agujero de seguridad aquí para las versiones antiguas de jQuery, algunas de las cuales probablemente todavía tengan una base de usuarios significativa: esas versiones [* detectarán y evaluarán explícitamente los guiones *] (https://github.com/jquery /jquery/blob/1.7/jquery.js#L6049) en el HTML pasado a '.html()'. Por lo tanto, incluso usar un 'textarea' no es suficiente para garantizar la seguridad aquí; Sugiero [no usar jQuery para esta tarea y escribir un código equivalente con la API DOM simple] (http://stackoverflow.com/a/1395954/1709587). (Sí, ese viejo comportamiento de jQuery es loco y horrible.) –

+0

Gracias por señalarlo. Sin embargo, la pregunta no incluye un requisito para verificar la inyección de scripts. La pregunta específicamente pregunta sobre html rendido por el servidor web. El contenido HTML guardado en un servidor web probablemente debería validarse para la inyección de scripts antes de guardar. –

0

una solución Javascript a que las capturas de los más comunes:

var map = {amp: '&', lt: '<', gt: '>', quot: '"', '#039': "'"} 
str = str.replace(/&([^;]+);/g, (m, c) => map[c]) 

este es el reverso de https://stackoverflow.com/a/4835406/2738039

+0

Si usa 'map [c] || '' 'no reconocidos no se mostrarán como' indefinidos ' – Eldelshell

+0

Cobertura muy limitada; -1. –

+1

+1, más es 'unescapeHtml (str) { var map = {amp: '&', lt: '<', le: '≤', gt: '>', ge: '≥', quot: ' "',' # 039 ':"' "} return str.replace (/ &([^;] +);/g, (m, c) => map [c] || '') }' –

1

Para los individuos de una sola línea:

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value; 

console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike')); 
1

En caso de que esté en busca de ella, como yo - mientras tanto hay un buen y seguro método de jQuery.

https://api.jquery.com/jquery.parsehtml/

Usted puede f.ex. escriba esto en la consola:

var x = "test &amp;"; 
> undefined 
$.parseHTML(x)[0].textContent 
> "test &" 

Así .parseHTML $ (x) devuelve una matriz, y si tiene el formato HTML dentro de su texto, el Array.length será mayor que 1.

0

Puede Lodash utilizar la función unescape/de escape https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape'; 

const str = unescape('fred, barney, &amp; pebbles'); 

str se convertirá en 'fred, barney, & pebbles'