Pregunta

What is the best way to look through an HTML element like a DIV and replace a specific piece of text with an HTML element. For example assume we have the tokens [b] and [/b]. I'd want to replace them with <b> and </b>, respectively.

The closest I have gotten this to work is the example below.

HTML to teplace:

<div id="response">This is some [b]GREAT[/b] stuff!</div>

Example #1:

$('#response').html(function() {
    return $(this).text().replace('[b]', '<b>');
});
$('#response').html(function () {
    return $(this).text().replace('[/b]', '</b>');
});

Result #1:

This is some GREAT stuff!


Example #2:

$('#response').html(function() {
    return $(this).text().replace('[b]', $('<b>'));
});
$('#response').html(function () {
    return $(this).text().replace('[/b]', $('</b>'));
});

Result #2:

This is some [object Object]GREAT[object Object] stuff!


Desired result:

This is some GREAT stuff!

Neither works. The former simply replaces the token with nothing, and the latter replaces it with an object in the browser, but not the HTML element as desired.

¿Fue útil?

Solución

Using a regular expression should do nicely:

$('#response').html(function() {
    return $(this).text().replace(/\[(\/?\w+)\]/g, "<$1>");
});

The benefit is that all tokens are replaced without having to name them individually.

Note that this opens the door for cross site scripting attacks!


If you want to transform tokens in the process (like [b] to <strong>) and whitelist them (and you definitely want to whitelist them to migitate XSS vulnerability), use a callback function as the replacement, like this:

$('#response').html(function () {
    var map = {
        b: "strong",
        i: "em",
        p: "p"
    };
    return $(this).text().replace(/\[(\/?)(\w+)\]/g, function ($0, $1, $2) {
        if (map.hasOwnProperty($2)) {
            return "<" + $1 + map[$2] + ">";
        } else {
            return $0;
        }
    });
});

And to make this a jQuery answer, here's how to turn all that into a fancy jQuery plugin for re-use:

$.fn.extend({
    unphpbb: (function () {
        var map = {
               b: "strong",
               i: "em",
               p: "p"
            },
            tokens = /\[(\/?)(\w+)\]/g,
            replacement = function ($0, $1, $2) {
                return map.hasOwnProperty($2) ? "<" + $1 + map[$2] + ">" : $0;
            },
            replaceTokens = function (i, html) {
                return html.replace(tokens, replacement);
            };

        return function () {
            return this.html(replaceTokens);
        };
    })()
});

used as

$("#response").unphpbb();

Of course using a bbcode parser on the server and sending properly scrubbed HTML to the client is the preferred method for solving this. Doing this on the client with a function like above is inferior in every way.

Otros consejos

You're looking to parse what is essentially BBCode. Patrick Gillespie wrote this JS Library that seems pretty simple to implement.

var result = XBBCODE.process({
    text: "Some bbcode to process here",
    removeMisalignedTags: false,
    addInLineBreaks: false
});
console.log("Errors: " + result.error);
console.dir(result.errorQueue);
console.log(result.html);// the HTML form of your BBCode

Your first effort looks better but you should probably do it in one call so as not to produce broken HTML between calls, eg

$('#response').html(function() {
    return $(this).text().replace('[b]', '<b>').replace('[/b]', '</b>');
});

You could even try this regular expression replacement instead

var rx = /\[(\/?[a-z]+)\]/g;
return $(this).text().replace(rx, '<$1>');

The regex approach would also let you define a set of allowed tags (so as not to support things like <script>), eg

var rx = /\[(\/?(b|i|strong|em|u|tt))\]/g;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top