Reputation: 157
Let's say I have a message that says "Hello there!" I wrapped every individual character (excluding whitespace) in a span, I have to do this because I'm using animeJS and want to animate the characters. The problem is that the message is being animated beautifully, but is breaking/wrapping at weird parts due to the fact that every character is in its own span. For example it looks like "Hello t /n here!", so it broke between the t and h. I only want it to break if theres a whitespace, not a span.
The html code excluding all the style classes looks like:
<div>
<span>H</span>
<span>e</span>
<span>l</span>
<span>l</span>
<span>o</span>
<div> </div>
<span>T</span>
<span>h</span>
<span>e</span>
<span>r</span>
<span>e</span>
<span>!</span>
</div>
The above code is the result of the below function I created:
const addSpan = (note: any, styles: any) => {
let note1 = note.replace(/./g, `<span class='letter' style=${styles}>$&</span>`);
let note2 = note1.replace(/<span class='letter' style=display:inline-block;> <\/span>/g, `<div> </div>`);
return DOMPurify.sanitize(
note2
);
};
I tried to add a div between the whitespace, but it causes newline breaks that I don't want. I just want the message to break only at whitespaces and not spans. What should I change about the whitespace wrappers?
I'm not sure if what I'm asking for is possible, any help?
EDIT: all the answers so far aren't understanding what I'm asking for. I ONLY want my message to break at the whitespace, NOT in between spans. I want my "hello" and "there" to stay together despite all the letters being in individual spans.
Upvotes: 2
Views: 500
Reputation: 48693
The letters should be spans and the words should be in-line blocks.
I even added support for paragraphs.
const text = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
`
const main = () => {
render(text, '.target')
}
const render = (text, target) => {
if (typeof target === 'string') {
target = document.querySelector(target)
}
removeChildren(target)
text.trim().split(/\n/g).forEach((paragraph, pIndex, pArr) => {
let paragraphEl = document.createElement('div')
paragraphEl.classList.add('paragraph')
paragraph.split(/\s+/g).forEach((word, wIndex, wArr) => {
let wordEl = document.createElement('div')
wordEl.classList.add('word')
word.split('').forEach((letter, lIndex, lArr) => {
let letterEl = document.createElement('span')
letterEl.classList.add('letter')
letterEl.textContent = letter
wordEl.appendChild(letterEl)
})
paragraphEl.appendChild(wordEl)
if (wIndex < wArr.length) {
let spaceEl = document.createElement('span')
spaceEl.classList.add('space')
spaceEl.innerHTML = ' '
paragraphEl.appendChild(spaceEl)
}
})
target.appendChild(paragraphEl)
})
}
const removeChildren = (el) => {
while (el.firstChild) {
el.firstChild.remove()
}
}
main()
.target {
width: 20em;
overflow: hidden;
border: thin solid grey;
}
.paragraph {
margin: 0.33em 0.5em;
}
.word {
display: inline-block;
}
.word:hover {
background: #FFA;
}
<div class="target"></div>
This is just a fun way to demonstrate the flow of creating the elements.
const text = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
`
const main = () => {
render(text, '.page')
}
const render = (text, target) => {
appendChildren(target, text.trim().split(/\n/g).map(
(paragraph, pIndex, pArr) => {
return {
tag: 'div',
options: {
class: 'paragraph',
data: { paragraphIndex : pIndex },
children: paragraph.split(/\s+/g).reduce(
(children, word, wIndex, wArr) => {
children.push({
tag: 'div',
options: {
class: 'word',
children: word.split('').map((letter) => {
return {
tag: 'span',
options: {
class: 'letter',
text: letter
}
}
})
}
})
if (wIndex < wArr.length) {
children.push({
tag: 'span',
options: {
classes: [ 'letter', 'space' ],
html: ' '
}
})
}
return children
}, [])
}
}
}), {
empty: true
})
}
const createEl = (tag, options) => {
if (typeof tag !== 'string') {
options = tag.options
tag = tag.tag
}
let opts = {
id: null,
class: null,
classes: [],
props: {},
attrs: {},
data: {},
text: null,
html: null,
parent: null,
children: [],
...options
}
let el = document.createElement(tag)
if (opts.id) el.id = opts.id
if (opts.class) el.className = opts.class
if (opts.classes) el.classList.add(...opts.classes)
Object.keys(opts.props).forEach(prop => el[prop] = opts.props[prop])
Object.entries(opts.attrs).forEach(attr => el.setAttribute.call(el, attr))
Object.assign(el.dataset, opts.data)
if (opts.text) el.textContent = opts.text
if (opts.html) el.innerHTML = opts.html
if (opts.parent) query(opts.parent).appendChild(el)
if (opts.children) appendChildren(el, opts.children)
return el
}
const appendChildren = (el, children, options) => {
let opts = {
empty: false,
...options
}
el = el == null ? document.body : query(el)
if (opts.empty) emptyEl(el)
children.forEach(child => {
if (isDomEntity(child)) {
el.appendChild(child)
} else {
let childEl = createEl(child.tag, child.options)
if (child.options.parent == null) {
el.appendChild(childEl)
}
}
})
}
const query = (selector) => {
return typeof selector === 'string' ?
document.querySelector(selector) : selector
}
const emptyEl = el => {
while (el.firstChild) {
el.firstChild.remove()
}
return el
}
const isDomEntity = entity => {
return typeof entity === 'object' && entity.nodeType !== undefined
}
main()
.page {
width: 20em;
overflow: hidden;
border: thin solid grey;
}
.paragraph {
margin: 0.33em 0.5em;
}
.word {
display: inline-block;
}
.word:hover {
background: #FFA;
}
<div class="page">Loading...</div>
Upvotes: 1