Allowing the user to paste clickable hyperlinks into a text area while editing

Viewed 61

I have a HTML form with a field in which the user can supply text. In that text they might want to include a link to another website amongst the plain text. Ideally, when they paste a URL from another source into the text area, it becomes clickable as they're editing. Is there a simple solution for implementing this feature without using external tools such as the one suggested in this answer?

Attempted Solutions

The closest I've gotten is by using a <div>contenteditable="true"</div> and dragging hyperlinks into the div. Unfortunately, when a div has contenteditable="true" anchors become un-clickable which was addressed here by holding the ctrl button to set contenteditable="false"; however, then we lose ctrl+v functionality. Additionally, I'm not sure how I will be able to submit the data if the tag is a <div> instead of <textarea>.

When using <textarea>, the link is copied as plain-text and isn't a clickable link. I believe anchors aren't allowed in a <textarea>.

I don't believe <input type="url"> works because the text area may contain content that isn't a URL.

Refer to snippet to see my attempts.

var content = document.getElementById('content');

document.addEventListener('keydown', function(event) {
  if (event.keyCode === 17) { // when ctrl is pressed
    content.contentEditable = false; // disable contentEditable
  }
}, false);

document.addEventListener('keyup', function(event) {
  if (event.keyCode === 17) { // when ctrl is released
    content.contentEditable = true; // reenable contentEditable
  }
}, false);
<textarea></textarea>
<div id="content" contenteditable="true"></div>
<input type="url">

2 Answers

Rather than ctrl click, you can add a button to toggle Preview/Edit like :

var content = document.getElementById('content');
var previewButton = document.getElementById('previewButton');

previewButton.addEventListener('click', function(event) {
  if (event.target.value === "Preview") { // when btn is first clicked
    event.target.value = "Edit";
    content.contentEditable = false; // disable contentEditable
  } else {  
    event.target.value = "Preview";
    content.contentEditable = true; // enablecontentEditable
  }
}, false);
<div id="content" contenteditable="true"></div>
<input type="button" value="Preview" id="previewButton" />

Using a mutationObserver that looks for new anchors in the text area, this script will set contentEditable = false when the user mouses over the anchor and toggle it back on when they mouse off.

var target = document.getElementById('content');

var observer = new MutationObserver(function(mutations) {
  let mutation = mutations.pop();
  if (mutation.type === 'childList') {
    let anchors = mutation.target.getElementsByTagName('a')

    for (let i = 0; i < anchors.length; i++) {
      let anchor = anchors[i]
      anchor.addEventListener('mouseover', function() {
        content.contentEditable = false; // disable contentEditable
      });
      anchor.addEventListener('mouseleave', function() {
        content.contentEditable = true; // disable contentEditable
        // restor caret position somehow
      });
    }
  }
});

var config = {
  attributes: false,
  childList: true,
  characterData: false
};

observer.observe(target, config);
    <div id="content" contenteditable="true"></div>

Issues

  • Restoring the caret to it's position before switching contentEditable to false is not done which reduces usability.

  • Not sure whether the text area can be submitted with the form when it is a <div> rather than a <textarea>.

Related