In Javascript, I'd like determine whether an element, say an A
element, exists inside a given range/textRange. The aim is to determine if the user's current selection contains a link. I am building a rich text editor control.
The range object has a commonAncestorContainer (W3C) or parentElement() (Microsoft) method which returns the closest common anscestor of all elements in the range. However, looking inside this element for A
elements won't work, since this common ancestor may also contain elements that aren't in the range, since the range can start or end part way through a parent.
How would you achieve this?
I ended up going with a solution like this:
var findinselection = function(tagname, container) {
var
i, len, el,
rng = getrange(),
comprng,
selparent;
if (rng) {
selparent = rng.commonAncestorContainer || rng.parentElement();
// Look for an element *around* the selected range
for (el = selparent; el !== container; el = el.parentNode) {
if (el.tagName && el.tagName.toLowerCase() === tagname) {
return el;
}
}
// Look for an element *within* the selected range
if (!rng.collapsed && (rng.text === undefined || rng.text) &&
selparent.getElementsByTagName) {
el = selparent.getElementsByTagName(tagname);
comprng = document.createRange ?
document.createRange() : document.body.createTextRange();
for (i = 0, len = el.length; i < len; i++) {
// determine if element el[i] is within the range
if (document.createRange) { // w3c
comprng.selectNodeContents(el[i]);
if (rng.compareBoundaryPoints(Range.END_TO_START, comprng) < 0 &&
rng.compareBoundaryPoints(Range.START_TO_END, comprng) > 0) {
return el[i];
}
}
else { // microsoft
comprng.moveToElementText(el[i]);
if (rng.compareEndPoints("StartToEnd", comprng) < 0 &&
rng.compareEndPoints("EndToStart", comprng) > 0) {
return el[i];
}
}
}
}
}
};
Where getrange() is another function of mine to get the current selection as a range object.
To use, call it like
var link = findselection('a', editor);
Where editor is the contenteditable element, or body in a designmode iframe.