errata
errata

Reputation: 6041

Find last character with jQuery

I have an HTML element which can contain plain text or other HTML elements:

<!-- plain text -->
<div class="content">
  SIMPLE TEXT.
</div>

<!-- or other html elements -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <table>
    <tr>
      <td>1</td>
      <td>2</td>
    </tr>
    <tr>
      <td>a</td>
      <td>b</td>
    </tr>
  </table>
</div>

<!-- more html elements -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <div>
    OTHER TEXT WITH MORE <span>HTML!</span>
  </div>
</div>

<!-- one more example -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <div>
    OTHER TEXT WITH MORE <span>HTML</span>!
  </div>
</div>

How can I append one more HTML element to the last printable character in my .content div, regardless of in which HTML element the character is?

Expected result:

<!-- plain text -->
<div class="content">
  SIMPLE TEXT.<span class="end"></span>
</div>

<!-- or other html elements -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <table>
    <tr>
      <td>1</td>
      <td>2</td>
    </tr>
    <tr>
      <td>a</td>
      <td>b<span class="end"></span></td>
    </tr>
  </table>
</div>

<!-- more html elements -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <div>
    OTHER TEXT WITH MORE <span>HTML!<span class="end"></span></span>
  </div>
</div>

<!-- one more example -->
<div class="content">
  SIMPLE <span>TEXT.</span>
  <div>
    OTHER TEXT WITH MORE <span>HTML</span>!<span class="end"></span>
  </div>
</div>

Upvotes: 3

Views: 2970

Answers (5)

DaniP
DaniP

Reputation: 38262

Here is my approach to solve this issue, the JQuery line by line and later the snippet example.

  • First loop trough each .content element:

    $('.content').each(function(){
    
  • Then store in a var which is the last character:

    var ch = $(this).text().trim().slice(-1);
    
  • Now since sometimes the last character can be just in a textNode and not inside a children of .content we need to differentiate this condition, we can identify the last nodeText children and the actual last element.

    var lastnode = $(this).contents().last();
    var textnode = $(this).contents().filter(function(){
      return this.nodeType === 3 && $.trim(this.nodeValue) !== '';;
    }).last();
    
  • Finally if the last children is a textNode we just need to append() the element to the .content parent, otherway find the last element that :contains our stored character and do the append():

$('.content').each(function(){
  var ch = $(this).text().trim().slice(-1);
  var lastnode = $(this).contents().last();
  var textnode = $(this).contents().filter(function(){
    return this.nodeType === 3 && $.trim(this.nodeValue) !== '';;
  }).last();
  
  if (lastnode[0] == textnode[0]) {
    $(this).append('<span class="end"></span>')
  } else {
    $(this).find(":contains('"+ch+"')").last().append('<span class="end"></span>')
  }
})
.end {
  width: 10px;
  height: 10px;
  margin:0 5px;
  background: purple;
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- plain text -->
<div class="content">
  SIMPLE TEXT.
</div>

<!-- or other html elements -->
<div class="content">
  SIMPLE<span>TEXT.</span>
  <table>
    <tr>
      <td>1</td>
      <td>2</td>
    </tr>
    <tr>
      <td>b</td>
      <td>b</td>
    </tr>
  </table>
</div>

Upvotes: 3

grimdog_john
grimdog_john

Reputation: 735

Old Answer

Another way is to use appendTo()

$("<span class="end"></span>").appendTo(".content");

This will append the element to just before the closing tag of anything that has the .content tag.

New Answer

Not the most elegant solution - but it does what you wanted it to

<script>
  //Loop each DOM element with a content class
  $(".content").each(function() {
    //Find out what the last Text Character is in the DOM Element
    //and use regex to determine the last position in the inner HTML
    //(Regex excludes tags)
    var positionToInsert = $(this).html().search("(?![^<]*>)" + 
         $(this).text().trim().charAt($(this).text().trim().length-1))+1;

    //Insert the <span> tag at the correct position and save to string 
    var newHtml = [$(this).html().slice(0, positionToInsert), 
                    "<span class='end'></span>",                                                                 
                    $(this).html().slice(positionToInsert)].join('');

    //Reset the HTML for the DOM element to the new string
    $(this).html(newHtml);
  });
</script>

Upvotes: -2

Daniel Lizik
Daniel Lizik

Reputation: 3144

Recurse through your element to get the last text node

https://jsfiddle.net/30ezoyau/

<div id="content">
  SIMPLE <span>TEXT.</span>
  <table>
    <tr>
      <td>1</td>
      <td>2</td>
    </tr>
    <tr>
      <td>a</td>
      <td id="last">b</td>
    </tr>
  </table>
</div>

js

// get your root element
const tree = document.querySelector('#content')

// flatten your tree into a list, then pop it to get last text node
const walk = (node = {}, list = []) => {
  // assuming you want the text node's parent node, not the text node itself
  if (/\w{1,}/i.test(node.textContent || '') && node.nodeName !== '#text')
    list.push(node)
  if (node.childNodes) {
    return [...node.childNodes].reduce((acc, child, i) => {
      const branch = walk(child, acc)
      return [...acc, ...branch]
    }, [])
  } else {
    return list
  }
}

// walk through tree to get last non-empty text node
const lastString = walk(tree).pop()

// append it
lastString.innerHTML = lastString.innerHTML + `<b> is last</b>`

// test
console.assert(
  document.getElementById('last').innerHTML === 'b<b> is last</b>',
  'should append span to last text'
)

Upvotes: 1

ControlAltDel
ControlAltDel

Reputation: 35106

to find the last text, you need a recursive function like this

function getLastText(element) {
  if (!element) return null;
  if (element.childNodes && element.childNodes.length > 0) {
    //alert('loop');
    for (var i = element.childNodes.length - 1; i >= 0; i++) {
      var glt = getLastText(element.childNodes[i]);
      if (glt != null) {
        return glt;
      }
    }
  }
  if (element.textContent && element.textContent.length > 0) {
    return element;
  }
  return null;
}

This will return the last element that has text contained within the element in initial getLastText call

Upvotes: 0

Tom
Tom

Reputation: 927

$('.content :last').append("<span class='end'></span>");

Upvotes: -1

Related Questions