Reputation: 6041
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
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
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
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
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