Retrieve parent node from selection (range) in Gecko and Webkit
I am trying to add an attribute when using a wysiwyg editor that uses "createLink" command. I thought it would be trivial to get back the node that is created after the browse executes that command.
Turns out, I am only able to grab this newly created node in IE. Any ideas?
The following code demonstrates the issue (debug logs at bottom show different output in each browser):
var getSelectedHTML = function() {
if ($.browser.msie) {
return this.getRange().htmlText;
} else {
var elem = this.getRange().cloneContents();
return $("<p/>").append($(elem)).html();
}
};
var getSelection = function() {
if ($.browser.msie) {
return this.editor.selection;
} else {
return this.iframe[0].contentDocument.defaultView.getSelection();
}
};
var getRange = function() {
var s = this.getSelection();
return (s.getRangeAt) ? s.getRangeAt(0) : s.createRange();
};
var getSelectedNode = function() {
var range = this.getRange();
var parent = range.commonAncestorContainer ? range.commonAncestorContainer :
range.parentElement ? range.parentElement():
range.item(0);
return parent;
};
// **** INSIDE SOME EVENT HANDLER ****
if ($.browser.msie) {
this.ec("createLink", true);
} else {
this.ec("createLink", false, prompt("Link URL:", "http://"));
}
var linkNode = $(this.getSelectedNode());
linkNode.attr("rel", "external");
$.log(linkNode.get(0).tagName);
// Gecko: "body"
// IE: "a"
// Webkit: "undefined"
$.log(this.getSelectedHTML());
// Gecko: "<a href="http://site.com">foo</a>"
开发者_如何学编程 // IE: "<A href="http://site.com" rel=external>foo</A>"
// Webkit: "foo"
$.log(this.getSelection());
// Gecko: "foo"
// IE: [object Selection]
// Webkit: "foo"
Thanks for any help on this, I've scoured related questions on SO with no success!
This is the code I've used to get the "parentNode" of the text cursor:
var getSelectedNode = function() {
var node,selection;
if (window.getSelection) {
selection = getSelection();
node = selection.anchorNode;
}
if (!node && document.selection) {
selection = document.selection
var range = selection.getRangeAt ? selection.getRangeAt(0) : selection.createRange();
node = range.commonAncestorContainer ? range.commonAncestorContainer :
range.parentElement ? range.parentElement() : range.item(0);
}
if (node) {
return (node.nodeName == "#text" ? node.parentNode : node);
}
};
I tweaked my IE method to approximate yours. Tested and working IE8, FF3.6, Safari4, Chrome5. I set up a jsbin preview that you can test with.
I have found that selection can get complicated and buggy across browsers. Throw in the magic of browser document editing, and it gets worse!
I took a look at how TinyMCE implements what I am trying to do, and took the same approach to modify jHtmlArea.
Basically, a link is created with a fake href. Then, it finds that dom element by searching for links with that particular href. You can then add any desired attributes and update the href with the actual url.
The solution above by gnarf is a great example of getting a selected node, and will work for most scenarios.
Below is the code for my work around:
var url = prompt("Link URL:", "http://");
if (!url) {
return;
}
// Create a link, with a magic temp href
this.ec("createLink", false, "#temp_url#");
// Find the link in the editor using the magic temp href
var linkNode = $(this.editor.body).find("a[href='#temp_url#']");
linkNode.attr("rel", "external");
// Apply the actual desired url
linkNode.attr("href", url);
It's a hacky workaround, but should work unless somebody makes two identical links.
this.getSelection()
seems to get the same in both needed browser, so:
var link=prompt('gimme link');
//add the thing
var text=this.getSelection();
var whatYouNeed=$('a:contains("'+text+'")[href="'+link+'"');
精彩评论