Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 754
Jason Avatar asked Mar 22 '10 17:03

Jason


2 Answers

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.

like image 169
gnarf Avatar answered Nov 17 '22 11:11

gnarf


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);
like image 22
Jason Avatar answered Nov 17 '22 10:11

Jason