jQuery text() function loses line breaks in IE

2019-01-25 12:44发布

问题:

This is quite a talked about problem on the jQuery forums but I can't figure out a solution for my situation.

I have an unordered list with some text elements in them... the user can create new list items via jQuery, which are saved to an SQL database. They can also edit existing list items. Also, because the text in each list item can get quite long, they have the option of 'hiding' each list item so it shows a truncated version of the string. This is handled by a custom jQuery plugin that truncates list items that are over a certain length... here's what each list item looks like once the truncate plugin has formatted it:

<li id="item_1">
<div class="note">
    This is the text that shows when this element is truncated <span style="display: none;" class="truncate_ellipsis">...</span>
<span style="display: inline;" class="truncate_more">This is the text that will be hidden if the user clicks on the 'hide' button</span>
</div>  
<div class="toggle_wrapper"> 
    <div class="note_toggle">
        <a href="#" class="truncate_more_link">Hide note</a>
    </div> 
    <div style="display: block;" class="note_controls"> 
        <span class="edit_note"><a href="#note_textfield" id="edit_note_1">Edit</a></span> | <span class="delete_note"><a href="#" id="delete_note_1">Delete</a></span> 
    </div> 
</div> 
</li>

The problem I have is that the user clicks on 'edit' and it takes the contents of the div with the class 'note' and assigns it to a text area. The user can then edit the text, and save it. I am using the following script to grab the contents of the div and assign it to the textarea:

$('.edit_note a').click(function(event){

    // Get the parent li of the 'edit' link that was clicked 
    var parentLi = $(this).closest('li');

    // fade out the li that's being edited
    parentLi.fadeOut('fast');

    // remove the '...' that shows when the note is truncated
    parentLi.find('.truncate_ellipsis').remove();

    // find the 'note' div within the li that's being edited
    var currentNote = parentLi.find('.note');

    // grab the id of this note... used when saving to the DB
    var noteId = $(this).attr('id').substr(10);

    // Set the current note id variable and editing status
    currentNoteId = noteId;
    currentNoteStatus = 'edit';

    //add the note ID as a hidden field to the text editor form
    $('input[name$="note_id"]').val(noteId);

    // grab the text from this note and add it to the text area
    // (textarea id is 'notes_content')
    $('#notes_content').val($.trim(currentNote.text()); // this is the text area

    // fade in the text area so the user can edit
    $('div.notes_textarea').fadeIn();

});

Once it's saved I'm using a function I found online to convert the line breaks to BRs, before assigning the contents of the text area back to the 'note' div within the appropriate list item:

function nl2br (str, is_xhtml) {   
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';    
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}

This all works fine in firefox / chrome / opera / safari... however, IE gets rid of the line breaks when it grabs the text via the jQuery text() function. This is discussed in the comments of this page on the jQuery documentation:

http://api.jquery.com/text/

Some people have had success by cloning the source element, wrapping it in 'pre' tags, then taking the contents of that and putting it in the text area. This works for line breaks but also brings over extra spaces, tabs etc. and it looks a bit of a mess. A better solution seems to be to use the html() jQuery function to grab the text, then replace br's for line breaks, and strip out any other html formatting.

I'm new to Javascript however, and I'm rubbish at regular expressions so I wouldn't have a clue how to do this and I'm running out of time! I need a regular expression, or just some help with string formatting to do the following:

  • remove all span tags from a string WITHOUT removing the contents of the span (my truncate function adds a span with the class "truncate_more" ... see html code above)

  • remove br's and replace them with newline characters that will display correctly in a textarea element and be consistent across browsers

OR.. if anyone knows of a better workaround to this IE / textarea / linebreak issue, I'd really appreciate an idiots guide :)

Quite a bit of information for what is probably a simple solution but I thought I'd try to explain the situation as best I could! Any help would be hugely appreciated....

EDIT: 'TheJuice's answer below worked for the br's / IE line breaks issue (thanks!), but I need to carry out a similar regex to get rid of the spans, as they only encapsulate some of the text. I tried the following, which seems to work fine in firefox but in IE the spans remain:

        var withBRs = currentNote.html();
    var textWithBreaks = withBRs.replace(/\<br\>/gi,'\r');
    textWithBreaks = textWithBreaks.replace(/\<.*span.*\>/g, '');

ANOTHER EDIT : 'TheJuice' solved this problem too... ignore my horrific regex and just do what he says if you find yourself in the same situation! Thanks to all who offered suggestions...

回答1:

Here is a far simpler solution to convert any HTML into plain text with line breaks based on the use of <br> or <p> tags.

function htmlDecodeWithLineBreaks(html) {
  var breakToken = '_______break_______',
      lineBreakedHtml = html.replace(/<br\s?\/?>/gi, breakToken).replace(/<p\.*?>(.*?)<\/p>/gi, breakToken + '$1' + breakToken);
  return $('<div>').html(lineBreakedHtml).text().replace(new RegExp(breakToken, 'g'), '\n');
}

For example, calling the following method:

htmlDecodeWithLineBreaks('line 1<br>line &amp; 2<br />' + 
  'line &gt; 3<p>paragraph 1</p>line 4')

returns:

line 1
line & 2
line > 3
paragraph 1
line 4

Hope that helps ;)



回答2:

I think something like this will work for you. http://jsfiddle.net/6qbxn/2/. I tested this in IE 7 & 8 & FF 4.0b7 and it works as expected.

JS:

var withBRs = $('#brText').html();
var textWithBreaks = withBRs.replace(/\<br\>/gi,'\r');
$('#area').text(textWithBreaks);

HTML:

<HTML>
<body>
    <div id="brText">Hello <br>How<br>Are<br>You?</div>
    <textarea rows="10" id="area">

    </textarea>
</body>

Edit: This addresses your second bullet point.