I’m trying to grab an element, which has <p> tags in it, <a>, and <span>. I’m trying to loop over each word in this element, and i need to be able to determine the words direct parent HTML element. I need to also be able to check the classes on the direct parent HTML
I’m just unsure of a starting point. Not sure how I can accomplish this. Is there a method I’ve overlooked?
One of the goals is to detect all text that does not have an anchor or a span as a direct parent (aka “KNOWN”, in this example) and wrap it with custom HTML.
Hey coothead, thank you, but I see that “KNOWN” is not logging in your first figcaption example. In my case, I need that to be detected (all words that do not have a span / anchor parent) and wrap that in a <span>. With it not logging in your example, I’m not sure how to modify it to target it .
Your example is looking for spans, but I need it to loop over individual words to be safer (unless there’s another way?)
DOM works best as a tree. Specifically, what you’re trying to do is a Pre-Order Tree Traversal of the root node.
In other words, instead of trying to figure out what the parent of each word is, figure out which parent the group of words belongs to.
function dive(root) {
$(root).contents().each(function(node) {
if(this.nodeType == 3 && this.wholeText.trim() !== '') {
console.log(this.wholeText+" belongs to "+this.parentNode.tagName+" with class "+this.parentNode.classList);
} else {
dive(this);
}
});
}
dive($('figcaption'));
Results in console:
KNOW belongs to SPAN with class purple
AND belongs to SPAN with class red
BE belongs to SPAN with class orange
KNOWN belongs to P with class
NOT KNOWN belongs to SPAN with class blue
AND belongs to SPAN with class grey
BE belongs to SPAN with class black
UNKNOWN belongs to SPAN with class green
Do you want spans around <span>each</span> <span>word</span>?
Can you show a couple of before and after examples in html?
To start with reading through your code I find difficult. A bit of line spacing etc would definitely help.
Below I have refactored your code in a way I personally think is clearer and less cluttered, but obviously the choice is yours:)
/*
Making 'dive' a separate function which takes a function/callback as an argument
means it can be used multiple times for different tasks.
*/
function dive(root, fn) {
/*
jquery's .each method take a callback function with two arguments (index, element)
it also binds 'this' to the element. this === element = true
*/
$(root).contents().each(function(i, elem) {
/*
I have swapped index and element around for the callback as I prefer
the element to be a key first argument and the index an optional second.
*/
fn(elem, i)
if($(elem).contents().length) dive(elem, fn)
});
}
/*
Keeping our 'childOf' function separate I think makes it less cluttered
and easier to debug.
*/
function childOf(elem) {
if(elem.nodeType === 3) {
let parent = elem.parentNode,
wholeText = elem.wholeText,
text = wholeText.trim()
if(text) {
if(parent.tagName !== 'SPAN' && parent.tagName !== 'A') {
let spanWrap = `<span class='red'>${text}</span>`;
/* not sure what you are trying to achieve here */
if(wholeText[0] === ' ') {
spanWrap = '*' + spanWrap;
}
if(wholeText[wholeText.length - 1] === " ") {
spanWrap = spanWrap + '#';
}
$(elem).replaceWith(spanWrap)
}
}
}
}
dive($('figcaption'), childOf)