Skip to content

Instantly share code, notes, and snippets.

@8mist
Last active November 16, 2022 08:18
Show Gist options
  • Save 8mist/c2176b96b69637e80e2450fbefabb621 to your computer and use it in GitHub Desktop.
Save 8mist/c2176b96b69637e80e2450fbefabb621 to your computer and use it in GitHub Desktop.
const createSpan = (text) => {
const node = document.createElement("span");
node.textContent = text;
return node;
};
const insertBetweenElementWhitespace = (elements) =>
elements.reduce((acc, span) => acc.concat(span, " "), []).slice(0, -1);
const spanifyText = text => {
const spans = text.wholeText
.split(" ")
.map(text => (text !== "" ? createSpan(text) : undefined))
.filter(node => node !== undefined);
return insertBetweenElementWhitespace(spans);
}
const replaceContentOf = (elementToReplace, replacementEls) => {
elementToReplace.innerHTML = "";
elementToReplace.append(...replacementEls);
}
const spanifyChildrenOf = element => {
const elements = [];
element.childNodes.forEach(node => {
if (node instanceof Text) {
const texts = spanifyText(node);
elements.push(...texts);
return;
}
const childs = spanifyChildrenOf(node);
replaceContentOf(node, childs);
elements.push(" ");
elements.push(node);
});
return elements;
}
const spanify = selector => {
const element = document.querySelector(selector);
const elements = spanifyChildrenOf(element);
replaceContentOf(element, elements);
}
spanify(".title");
export interface SplitParams {
element: HTMLElement;
expression?: string;
append?: boolean;
}
class SplitText {
protected static forbiddenElements: string[] = ['a', 'strong', 'em'];
public static split({
element,
expression = ' ',
append = false
}: SplitParams): NodeListOf<HTMLSpanElement> {
const words: string[] = this.splitText(element.innerHTML.toString().trim(), expression);
const innerHTML: string = words.reduce((acc: string, line: string) => {
if (line.indexOf('<br>') > -1) {
const lines: string[] = line.split('<br>');
acc += lines.reduce((acc: string, line: string, index: number) => {
const lineBreak = index > 0 ? '<br>' : '';
return `${acc}${lineBreak}${this.parseLine(line)}`;
}, '');
} else {
acc += this.parseLine(line);
}
return acc;
}, '');
element.innerHTML = innerHTML;
const spans: NodeListOf<HTMLSpanElement> = element.querySelectorAll('span');
if (append) {
this.appendSpans(spans);
}
return spans;
}
public static calculate(spans: any): any[] {
const lines: any[] = [];
const words: any[] = [];
let position: any = spans[0].offsetTop;
spans.forEach((span: any, index: number) => {
if (span.offsetTop === position) {
words.push(span);
}
if (span.offsetTop !== position) {
lines.push(words);
words.splice(0, words.length);
words.push(span);
position = span.offsetTop;
}
if (index + 1 === spans.length) {
lines.push(words);
}
});
return lines;
}
private static splitText(text: string, expression: string): string[] {
const textSplitted = text.split('<br>');
let words: string[] = [];
textSplitted.forEach((item: string, idx: number) => {
const innerHTML: any[] = [];
let isForbidden: boolean = false;
let forbiddenElement: string = '';
if (idx > 0) {
words.push('<br>');
}
words = words.concat(item.split(expression));
for (const word of words) {
if (!isForbidden && this.checkIfTagIsForbidden({ word: word, beginTag: true })) {
forbiddenElement = '';
isForbidden = true;
}
if (isForbidden) {
forbiddenElement += ` ${word}`;
}
if (isForbidden && this.checkIfTagIsForbidden({ word: word })) {
innerHTML.push(forbiddenElement);
forbiddenElement = '';
}
if (!isForbidden && forbiddenElement === '') {
innerHTML.push(word);
}
if (isForbidden && this.checkIfTagIsForbidden({ word: word })) {
isForbidden = false;
}
}
words = innerHTML;
});
return words;
}
private static parseLine(line: string): string {
if (line === '') {
return line;
}
if (line === ' ') {
return '&nbsp;';
}
const lineTrimmed = line.trim();
return lineTrimmed === '<br>'
? '<br>'
: `<span>${lineTrimmed}</span>` + (lineTrimmed.length > 1 ? ' ' : '');
}
private static appendSpans(spans: NodeListOf<HTMLSpanElement>): void {
spans.forEach((span: HTMLSpanElement) => {
if (!span || !span.textContent) {
return;
}
const isSingleLetter = span.textContent.length === 1;
const isNotEmpty = span.innerHTML.trim() !== '';
const isNotAndCharacter = span.textContent !== '&';
const isNotDashCharacter = span.textContent !== '-';
if (isSingleLetter && isNotEmpty && isNotAndCharacter && isNotDashCharacter) {
span.innerHTML = `${span.textContent}&nbsp;`;
}
});
}
private static checkIfTagIsForbidden({
word,
beginTag = false
}: {
word: string;
beginTag?: boolean;
}): boolean {
return this.forbiddenElements.some(forbiddenElement =>
word.includes(`${beginTag ? `<${forbiddenElement}` : `/${forbiddenElement}>`}`)
);
}
}
export default SplitText;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment