Html SpecialChars tương đương trong Javascript?


167

Rõ ràng, điều này khó tìm thấy hơn tôi nghĩ. Và nó thậm chí rất đơn giản ...

Có một chức năng tương đương với htmlspecialchars của PHP được tích hợp trong Javascript không? Tôi biết việc tự thực hiện điều đó khá dễ dàng, nhưng sử dụng chức năng tích hợp, nếu có, chỉ là đẹp hơn.

Đối với những người không quen thuộc với PHP, htmlspecialchars dịch các thứ như <htmltag/>vào&lt;htmltag/&gt;

Tôi biết điều đó escape()encodeURI()không làm việc theo cách này.


php đã có một số công cụ thực sự tốt, var_dump, print_r, htmlspecialchars, v.v. Thật không may, tôi nghi ngờ là không giống với js. cảnh báo js rất kém Một cách nhanh chóng để thấy rằng một số chuỗi bất ngờ (và vô hình trong hộp cảnh báo) đang đến, là cảnh báo độ dài chuỗi thay vì chuỗi itlef.
Melsi

Bản sao có thể có của Thoát chuỗi HTML với jQuery
nhahtdh

Xem stackoverflow.com/a/12034334/8804293 , nó có một câu trả lời tuyệt vời
Elijah Mock

Câu trả lời:


330

Có một vấn đề với mã giải pháp của bạn - nó sẽ chỉ thoát khỏi lần xuất hiện đầu tiên của mỗi ký tự đặc biệt. Ví dụ:

escapeHtml('Kip\'s <b>evil</b> "test" code\'s here');
Actual:   Kip&#039;s &lt;b&gt;evil</b> &quot;test" code's here
Expected: Kip&#039;s &lt;b&gt;evil&lt;/b&gt; &quot;test&quot; code&#039;s here

Đây là mã hoạt động đúng:

function escapeHtml(text) {
  return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
}

Cập nhật

Đoạn mã sau sẽ tạo ra kết quả giống hệt như trên, nhưng nó hoạt động tốt hơn, đặc biệt là trên các khối văn bản lớn (cảm ơn jbo5112 ).

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

5
Một điều tuyệt vời về chức năng này là nó hoạt động trong node.js không có dom theo mặc định
booyaa

6
Nó nhanh hơn để sử dụng một chức năng thay thế và ánh xạ duy nhất, và thay thế duy nhất quy mô tốt hơn nhiều. ( jsperf.com/escape-html-special-chars/11 )
jbo5112

1
@ jbo5112 điểm tốt, tôi đã không nhận ra JS cho phép gọi lại để thay thế. Mặc dù vậy, mã này dễ hiểu hơn và tôi nghi ngờ rằng việc cạo một vài phần nghìn giây của escHtml () sẽ tạo ra sự khác biệt trừ khi bạn gọi nó hàng trăm lần liên tiếp vì một số lý do.
Kip

Điều này sẽ làm biến dạng các URL trong văn bản khiến chúng không thể sử dụng được cho các plugin như Autolinker.js . Có cách nào để tiếp cận điều này?
Radek Matěj

4
@ RadekMatěj Ngay cả trong trường hợp đó, nó hoàn toàn hợp lệ (tốt nhất là tôi sẽ tranh luận) cho cả hai ký hiệu được mã hóa khi được sử dụng trong tài liệu HTML. Tôi vẫn sẽ coi đó là một lỗi với plugin.
Kip

31

Đó là mã hóa HTML. Không có chức năng javascript gốc để làm điều đó, nhưng bạn có thể google và nhận được một số cái được thực hiện độc đáo.

Ví dụ: http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

EDIT:
Đây là những gì tôi đã thử nghiệm:

var div = document.createElement('div');
  var text = document.createTextNode('<htmltag/>');
  div.appendChild(text);
  console.log(div.innerHTML);

Đầu ra: &lt;htmltag/&gt;


Quá tệ, tôi sẽ phải sử dụng một chức năng tùy chỉnh sau đó.
Bart van Heukelom

Bạn có thể thử phương pháp trong liên kết tôi đã đưa vào bài viết của mình. Khái niệm khá gọn gàng thực sự.
okw

@okw: Ok, đầu tiên bạn liên kết với điều này: yuki-onna.co.uk/html/encode.html , nơi thực hiện chính xác những gì encodeURIComponentkhông và hoàn toàn không phải những gì OP yêu cầu. Vì vậy, bạn có thể chỉnh sửa xin vui lòng? Tôi dường như không thể hoàn tác -1 của mình.
Lưỡi liềm tươi

Yah, mã của trang đó trông hợp lý nhưng tôi đã không kiểm tra nó. Liên kết mới mặc dù hoạt động, tôi đã tự xác minh nó. Tôi đã cập nhật bài viết một thời gian trước.
okw

@BeauCielBleu: Không. Các nút duy nhất được tạo là một divphần tử và một nút văn bản. Tạo một nút văn bản với văn bản `<img src = bogus onerror = alert (1337)>` sẽ chỉ tạo một nút văn bản, không phải là một imgphần tử.
Tim Down

26

Đáng đọc: http://bigdingus.com/2007/12/29/html-escaping-in-javascript/

escapeHTML: (function() {
 var MAP = {
   '&': '&amp;',
   '<': '&lt;',
   '>': '&gt;',
   '"': '&#34;',
   "'": '&#39;'
 };
  var repl = function(c) { return MAP[c]; };
  return function(s) {
    return s.replace(/[&<>'"]/g, repl);
  };
})()

Lưu ý : Chỉ chạy cái này một lần. Và đừng chạy nó trên các chuỗi đã được mã hóa, ví dụ như &amp;trở thành&amp;amp;


3
Đây phải là câu trả lời được chấp nhận và bình chọn cao nhất. Tôi không chắc tại sao nó không có phiếu bầu. Đây là điểm chuẩn là nhanh nhất với cả chuỗi kết quả tìm kiếm dài (326KB Google) và chuỗi đầu vào ngắn trên jsperf ( jsperf.com/escape-html-special-chars/11 ). Hãy bỏ phiếu này lên.
jbo5112

Sự khác biệt giữa câu trả lời này có số phiếu cao nhất là gì ?. Tại sao chức năng bên trong bổ sung?. Một lời giải thích có thể giúp người dùng hiểu rõ hơn
Kosem

19

Với jQuery, nó có thể như thế này:

var escapedValue = $('<div/>').text(value).html();

Từ câu hỏi liên quan Thoát chuỗi HTML bằng jQuery

Như đã đề cập trong bình luận, trích dẫn kép và trích dẫn đơn được để nguyên cho việc thực hiện này. Điều đó có nghĩa là không nên sử dụng giải pháp này nếu bạn cần tạo thuộc tính phần tử dưới dạng chuỗi html thô.


2
bất kỳ ý tưởng nào nếu có bất kỳ chi phí nào cho việc này - thêm một đối tượng giả vào DOM?
Kip

và có bất kỳ lợi thế nào khác (giả sử, nếu bạn có các ký tự unicode hoặc một cái gì đó)?
Kip

4
Một cái gì đó tôi tìm thấy với điều này: dấu ngoặc kép và dấu ngoặc đơn được để nguyên. Điều đó làm cho vấn đề này nếu bạn muốn sử dụng nó trong một giá trị thuộc tính.
Kip

1
Đối với các đoạn văn bản nhỏ, việc này mất 30 lần miễn là chạy tất cả các thay thế. Nó có quy mô tốt hơn mặc dù. Với thứ gì đó khổng lồ như trang kết quả tìm kiếm của Google (326KB), nó nhanh hơn 25-30% so với thay thế hoặc thực hiện điều này trong javascript thẳng. Tuy nhiên, tất cả đều liên tục thua một thay thế duy nhất và một chức năng ánh xạ.
jbo5112

4
Làm thế nào mọi người bỏ phiếu cho câu trả lời này: câu trả lời có jquery: +1 - KHÔNG thoát khỏi dấu ngoặc đơn và dấu ngoặc kép: ummmm .. (gãi đầu) .. +1. <!-- Caps rage begin --> Câu trả lời này phải có điểm NEGECT vì nó KHÔNG NGAY LẬP TỨC ĐỂ TRẢ LỜI CÂU HỎI "Tương đương với Html SpecialChars". <!-- Caps rage end -->nó-không-không-thoát-trích dẫn-jesus-christ-và-các vị thần khác. OMG bạn jquery người.
Sharky

19

Đây là một chức năng để thoát HTML:

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

Và để giải mã:

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

6

Underscore.js cung cấp một chức năng cho việc này:

_.escape(string)

Thoát một chuỗi để chèn vào HTML, thay thế các ký tự &, <,>, "và '.

http://underscorejs.org/#escape

Đây không phải là một chức năng Javascript tích hợp, nhưng nếu bạn đã sử dụng Underscore thì đó là một cách thay thế tốt hơn so với việc viết chức năng của riêng bạn nếu chuỗi chuyển đổi của bạn không quá lớn.


5

Tuy nhiên, một điều nữa là từ bỏ hoàn toàn ánh xạ ký tự và thay vào đó chuyển đổi tất cả các ký tự không mong muốn thành các tham chiếu ký tự số tương ứng của chúng, ví dụ:

function escapeHtml(raw) {
    return raw.replace(/[&<>"']/g, function onReplace(match) {
        return '&#' + match.charCodeAt(0) + ';';
    });
}

Lưu ý rằng RegEx được chỉ định chỉ xử lý các ký tự cụ thể mà OP muốn thoát, nhưng tùy thuộc vào ngữ cảnh mà HTML thoát sẽ được sử dụng, các ký tự này có thể không đủ. Bài viết của Ryan Grove Có nhiều thoát HTML hơn &, <,> và " là một bài đọc tốt về chủ đề này. Và tùy thuộc vào ngữ cảnh của bạn, RegEx sau đây rất có thể cần thiết để tránh tiêm XSS:

var regex = /[&<>"'` !@$%()=+{}[\]]/g

3
String.prototype.escapeHTML = function() {
        return this.replace(/&/g, "&amp;")
                   .replace(/</g, "&lt;")
                   .replace(/>/g, "&gt;")
                   .replace(/"/g, "&quot;")
                   .replace(/'/g, "&#039;");
    }

mẫu vật :

var toto = "test<br>";
alert(toto.escapeHTML());

3

Có thể bạn không cần một chức năng như vậy. Vì mã của bạn đã có trong trình duyệt *, bạn có thể truy cập trực tiếp vào DOM thay vì tạo và mã hóa HTML sẽ phải được giải mã ngược bởi trình duyệt thực sự được sử dụng.

Sử dụng thuộc innerTexttính để chèn văn bản đơn giản vào DOM một cách an toàn và nhanh hơn nhiều so với sử dụng bất kỳ chức năng thoát nào được trình bày. Thậm chí nhanh hơn việc gán một chuỗi tiền mã hóa tĩnh cho innerHTML.

Sử dụng classListđể chỉnh sửa các lớp, datasetđể đặt data-thuộc tính và setAttributecho người khác.

Tất cả những điều này sẽ xử lý thoát cho bạn. Chính xác hơn, không cần thoát và không mã hóa sẽ được thực hiện bên dưới **, vì bạn đang làm việc xung quanh HTML, biểu diễn văn bản của DOM.

// use existing element
var author = 'John "Superman" Doe <john@example.com>';
var el = document.getElementById('first');
el.dataset.author = author;
el.textContent = 'Author: '+author;

// or create a new element
var a = document.createElement('a');
a.classList.add('important');
a.href = '/search?q=term+"exact"&n=50';
a.textContent = 'Search for "exact" term';
document.body.appendChild(a);

// actual HTML code
console.log(el.outerHTML);
console.log(a.outerHTML);
.important { color: red; }
<div id="first"></div>

* Câu trả lời này không dành cho người dùng JavaScript phía máy chủ (Node.js, v.v. )

** Trừ khi bạn chuyển đổi rõ ràng thành HTML thực tế sau đó. Ví dụ: bằng cách truy cập innerHTML- đây là những gì xảy ra khi bạn chạy được $('<div/>').text(value).html();đề xuất trong các câu trả lời khác. Vì vậy, nếu mục tiêu cuối cùng của bạn là chèn một số dữ liệu vào tài liệu, bằng cách thực hiện theo cách này, bạn sẽ thực hiện công việc hai lần. Ngoài ra, bạn có thể thấy rằng trong HTML kết quả, không phải mọi thứ đều được mã hóa, chỉ có mức tối thiểu cần thiết để nó hợp lệ. Nó được thực hiện một cách phụ thuộc vào ngữ cảnh, đó là lý do tại sao phương thức jQuery này không mã hóa các trích dẫn và do đó không nên được sử dụng như một công cụ thoát hiểm cho mục đích chung. Báo giá thoát là cần thiết khi bạn xây dựng HTML dưới dạng một chuỗi với dữ liệu không đáng tin cậy hoặc có chứa trích dẫn tại vị trí của giá trị thuộc tính. Nếu bạn sử dụng API DOM, bạn hoàn toàn không phải lo lắng về việc thoát.


Cảm ơn vì điều đó! Tôi đã dành thời gian dài để tìm kiếm một giải pháp đơn giản như vậy. Một điều quan trọng tôi đã khám phá là nếu văn bản của bạn chứa các dòng mới, thì bạn sẽ phải thay thế chúng bằng các ngắt dòng HTML (đại loại như el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')) hoặc đặt thuộc tính CSS white-spacethành prehoặcpre-wrap
stellatedHexahedron

@stellatedHexahedron, cảm ơn vì đã nêu ra vấn đề này. Tôi đã thay đổi câu trả lời của tôi để đề nghị innerTextthay vì textContent. Mặc dù chậm hơn một chút và có một số khác biệt khác khi đọc thuộc tính, nhưng nó trực quan hơn ở chỗ nó <br>tự động thay thế khi gán cho nó.
người dùng

2

Đối với người dùng Node.JS (hoặc người dùng sử dụng thời gian chạy Jade trong trình duyệt), bạn có thể sử dụng chức năng thoát của Jade.

require('jade').runtime.escape(...);

Không có ý nghĩa trong việc tự viết nó nếu người khác đang duy trì nó. :)


1

Tôi đang giải thích một chút về câu trả lời của okw.

Bạn có thể sử dụng các chức năng DOM của trình duyệt cho điều đó.

var utils = {
    dummy: document.createElement('div'),
    escapeHTML: function(s) {
        this.dummy.textContent = s
        return this.dummy.innerHTML
    }
}

utils.escapeHTML('<escapeThis>&')

Điều này trở lại &lt;escapeThis&gt;&amp;

Nó sử dụng hàm tiêu chuẩn createElementđể tạo một phần tử vô hình, sau đó sử dụng hàm textContentđể đặt bất kỳ chuỗi nào làm nội dung của nó và sau đó innerHTMLlấy nội dung trong biểu diễn HTML của nó.


0
function htmlspecialchars(str) {
 if (typeof(str) == "string") {
  str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
  str = str.replace(/"/g, "&quot;");
  str = str.replace(/'/g, "&#039;");
  str = str.replace(/</g, "&lt;");
  str = str.replace(/>/g, "&gt;");
  }
 return str;
 }

0

Hy vọng điều này sẽ thắng cuộc đua do hiệu suất của nó và quan trọng nhất không phải là logic chuỗi bằng cách sử dụng .replace ('&', '&'). Thay thế ('<', '<') ...

var mapObj = {
   '&':"&amp;",
   '<':"&lt;",
   '>':"&gt;",
   '"':"&quot;",
   '\'':"&#039;"
};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");

function escapeHtml(str) 
{   
    return str.replace(re, function(matched)
    {
        return mapObj[matched.toLowerCase()];
    });
}

console.log('<script type="text/javascript">alert('Hello World');</script>');
console.log(escapeHtml('<script type="text/javascript">alert('Hello World');</script>'));

0

Đảo ngược một:

function decodeHtml(text) {
    return text
        .replace(/&amp;/g, '&')
        .replace(/&lt;/ , '<')
        .replace(/&gt;/, '>')
        .replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'");
}

Câu hỏi không hỏi làm thế nào để giải mã các thực thể. Điều này trái ngược với những gì câu hỏi đang yêu cầu.
Quentin

Điều này sẽ chỉ thay thế các trường hợp đầu tiên&lt;&gr;trong một chuỗi.
Quentin

Điều này sẽ chỉ giải mã năm ký tự (bên ngoài các tài liệu không phải là Unicode) phải được thoát, nó sẽ không giải mã được các ký tự có thể được thoát.
Quentin

Điều này không tính đến các quy tắc khi dấu chấm phẩy là tùy chọn.
Quentin

Nếu HTML nói : To write a greater than sign in HTML type &amp;gt;, nó sẽ hiển thị không chính xác >thay vì&gt;
Quentin

0

OWASP khuyến nghị rằng "[e] xcept cho các ký tự chữ và số, [bạn nên] thoát tất cả các ký tự có giá trị ASCII nhỏ hơn 256 với &#xHH;định dạng (hoặc một thực thể được đặt tên nếu có) để ngăn chặn chuyển ra khỏi thuộc tính [một]."

Vì vậy, đây là một chức năng thực hiện điều đó, với một ví dụ sử dụng:

function escapeHTML(unsafe) {
  return unsafe.replace(
    /[\u0000-\u002F]|[\u003A-\u0040]|[\u005B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).substr(-4, 4) + ';'
  )
}
document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('this should break it! " | / % * + , - / ; < = > ^') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
<div></div>


-1
function htmlEscape(str){
    return str.replace(/[&<>'"]/g,x=>'&#'+x.charCodeAt(0)+';')
}

Giải pháp này sử dụng mã số của các ký tự, ví dụ <được thay thế bằng &#60;.

Mặc dù hiệu suất của nó kém hơn một chút so với giải pháp sử dụng bản đồ , nhưng nó có những ưu điểm:

  • Không phụ thuộc vào thư viện hoặc DOM
  • Khá dễ nhớ (bạn không cần phải ghi nhớ 5 ký tự thoát HTML)
  • Mã nhỏ
  • Nhanh chóng hợp lý (vẫn nhanh hơn 5 chuỗi thay thế)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.