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 = rangemonAncestorContainer ? rangemonAncestorContainer :
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="">foo</a>"
// IE: "<A href="" 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!
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!
Share Improve this question asked Mar 22, 2010 at 17:02 JasonJason 2,7411 gold badge27 silver badges28 bronze badges 2- @jason - Is there something wrong with my answer? I haven't heard any feedback from you yet... – gnarf Commented Mar 31, 2010 at 21:18
- Sorry, your example does work, and code is nearly the same as what I have just more compact together. However, it still does not work for my implementation, I'm wondering if it might have something to do with the iframe or browser editor interfering. I'll update when I've got it fully working. Thanks for the help! – Jason Commented Apr 1, 2010 at 21:01
3 Answers
Reset to default 15This 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+'"');