0

Let's say I have this html

<strong><a href="url">Link</a></strong>

and I want to replace this with something else programatically. I select this with the mouse and call

var sel = window.getSelection()

The content of sel is however a text, and its parentNode is the link node a (and its parentNode is the <strong> element I was looking for).

Can I get semantic elements like e.g. strong, b, em in a selection?

Use case: I want to select some text in a wysiwyg editor (html) and replace it with a link.

swenedo
  • 3,074
  • 8
  • 30
  • 49
  • You mean you want to get the `strong` element directly, instead of the text node? – Oriol Aug 28 '15 at 16:45
  • @Oriol Yes, if I select some text that is e.g. inside `strong`+`em`+`i`, I would like to get the outermost node and not the text node. I _could_ walk up the chain, but that's not straightforward. I don't know beforehand if the text is wrapped in semantic elements – swenedo Aug 28 '15 at 17:36
  • What if only some text inside the `strong` is selected? What if the selection begins inside `strong` but ends outside it, or vice versa? What if it starts and ends outside it, but contains it? Selections are much complicated to handle properly. – Oriol Aug 28 '15 at 17:42
  • @Oriol I'm starting to realise they are complicated. In your example, I would expect it to automatically close the `strong` for me before my selection (when I delete the content with `range.deleteContents();`) – swenedo Aug 28 '15 at 17:45

2 Answers2

1

You can use the jQuery selector feature to search in the string for particular tag:

$('#btn').on('click', function() {
  var sel = getSelectionHtml();
  alert(sel.toString())
  var anchor = $(sel).find('a');
  var id = anchor.attr('id');
  // here DOM manipulation starts
  $('#' + id).html('Click here');
});

function getSelectionHtml() {
  var html = "";
  if (typeof window.getSelection != "undefined") {
    var sel = window.getSelection();
    if (sel.rangeCount) {
      var container = document.createElement("div");
      for (var i = 0, len = sel.rangeCount; i < len; ++i) {
        container.appendChild(sel.getRangeAt(i).cloneContents());
      }
      html = container.innerHTML;
    }
  }
  else if (typeof document.selection != "undefined") {
    if (document.selection.type == "Text") {
      html = document.selection.createRange().htmlText;
    }
  }
  return html;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span>This is sample text</span>
<br/>
<br/>
<strong><a href="url" id="myLink">Link</a></strong>
<span>This is another text</span>
<br/>
<br/>
<span>Select two above lines and click the button below</span>
<br/>
<br/>
<input type="button" id="btn" value="Click to see selected HTML" />

The function used to obtain HTML selection was reused from this page

Community
  • 1
  • 1
MaxZoom
  • 7,619
  • 5
  • 28
  • 44
  • Cool! :) However, it only works if I select more than the bold link. My questions was how I can select this html: `Link`. If I only select that in your example, the alert only says "Link" – swenedo Sep 02 '15 at 08:01
  • Because in that scenario only `Link` text was selected. The **selection range** seems to unnecessary broaden this question, for which you can reference the [official page](https://developer.mozilla.org/en-US/docs/Web/API/Range). – MaxZoom Sep 02 '15 at 14:21
  • I thought I selected a bold link with the text content "Link" in my scenario. Why is **selection range** to broad for this question? Is your answer that it's not possible to select a semantic element like I'm asking in the question? – swenedo Sep 02 '15 at 14:38
  • That question has been already discussed somewhere else , ie. [here](http://stackoverflow.com/q/7215479/4454454) – MaxZoom Sep 02 '15 at 14:52
0

Are you sure that there wouldn't be other <strong> tags on the page? This isn't unique at all, so you would be running a risk of selecting/replacing the wrong element. You could do something where you're finding the first or closest, but this may still be risky (could even grab this in a text-based ad that you're running).

That said, if you feel comfortable with the approach, you could try something like this instead:

document.getElementsByTagName("strong")[0].innerHTML = "<a href='url'>Some words</a>";

That would grab the first <strong> tag on the page and inject the link for you.

Tim Barnes
  • 46
  • 6
  • I don't need to search like that. I select some text with the mouse, and then want to replace whatever I selected with a new node. I first select with `sel = window.getSelection();` and then make a range with `range = sel.getRangeAt(0);` and then deletes the content with `range.deleteContents();` and lastly insert my new element `a` with `range.insertNode(a);` :-) – swenedo Aug 28 '15 at 17:42