How to make clickable anchor in contentEditable div?
Just wrap the link in another div, like so:
<div contentEditable="true">
<div contentEditable="false">
Bla bla <a href="http://google.com">Google</a> Bla bla
</div>
</div>
how to make clickable links after changing a contentEditable div?
If we assume an explanation always comes before a link every time, you can split the html in the #story and link the every second text, and you can disable the last clicked button to avoid any bugs.
// Edit : now works with all randomly placed links
var story = $("#story").html();
$('#btnedit').click(function(){$('#story').prop('contentEditable', true).focus();$('#story').html(story)
$("#btnsave").css("pointer-events", "all");$(this).css("pointer-events", "none");});
$('#btnsave').click(function(){ $(this).css("pointer-events", "none"); $("#btnedit").css("pointer-events", "all"); $('#story').prop('contentEditable', false).blur(); var newContent = ""; story = $("#story").html();
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; newContent = story.replace(exp,"<a href=\"$1\">$1</a>");
$("#story").html(newContent);});
.story{white-space:pre-wrap;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button id='btnedit'>EDIT</button><button id='btnsave'>SAVE</button>
<div class='story' id='story'>lorem ipsumhttps://www.youtube.com/lorem ipsumhttps://www.google.com/</div>
How to edit a link within a contentEditable div
I'm pretty sure this is what you were looking for, however I used jQuery just to make the concept a little easier to mock. jsbin preview available, so go look at it. If anyone is able to convert this to pure JS for the sake of the answer, I have made it a community wiki.
It works by binding to the keyup/click events on the editable div, then checking for the node that the users caret is being placed at using window.getSelection()
for the Standards, or document.selection
for those IE people. The rest of the code handles popping/handling the edits.
jQuery methods:
function getSelectionStartNode(){
var node,selection;
if (window.getSelection) { // FF3.6, Safari4, Chrome5 (DOM Standards)
selection = getSelection();
node = selection.anchorNode;
}
if (!node && document.selection) { // IE
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);
}
}
$(function() {
$("#editLink").hide();
$("#myEditable").bind('keyup click', function(e) {
var $node = $(getSelectionStartNode());
if ($node.is('a')) {
$("#editLink").css({
top: $node.offset().top - $('#editLink').height() - 5,
left: $node.offset().left
}).show().data('node', $node);
$("#linktext").val($node.text());
$("#linkhref").val($node.attr('href'));
$("#linkpreview").attr('href', $node.attr('href'));
} else {
$("#editLink").hide();
}
});
$("#linktext").bind('keyup change', function() {
var $node = $("#editLink").data('node');
$node.text($(this).val());
});
$("#linkhref").bind('keyup change', function() {
var $node = $("#editLink").data('node');
$node.attr('href', $(this).val());
$node.and('#linkpreview').attr('href',$(this).val());
});
});
Insert link in contenteditable element
document.execCommand()
does this for you in all major browsers:
document.execCommand("CreateLink", false, "http://stackoverflow.com/");
To preserve the selection while your link dialog is displayed, you can use the following functions:
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
var ranges = [];
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
ranges.push(sel.getRangeAt(i));
}
return ranges;
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(savedSel) {
if (savedSel) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedSel.length; i < len; ++i) {
sel.addRange(savedSel[i]);
}
} else if (document.selection && savedSel.select) {
savedSel.select();
}
}
}
jsFiddle example: http://jsfiddle.net/JRKwH/1/
UPDATE
To get hold of the link(s) created (if any were created at all) is tricky. You could use my own Rangy library:
var sel = rangy.getSelection();
if (sel.rangeCount) {
var links = sel.getRangeAt(0).getNodes([1], function(el) {
return el.nodeName.toLowerCase() == "a";
});
alert(links.length);
}
... or something like the following:
function getLinksInSelection() {
var selectedLinks = [];
var range, containerEl, links, linkRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
linkRange = document.createRange();
for (var r = 0; r < sel.rangeCount; ++r) {
range = sel.getRangeAt(r);
containerEl = range.commonAncestorContainer;
if (containerEl.nodeType != 1) {
containerEl = containerEl.parentNode;
}
if (containerEl.nodeName.toLowerCase() == "a") {
selectedLinks.push(containerEl);
} else {
links = containerEl.getElementsByTagName("a");
for (var i = 0; i < links.length; ++i) {
linkRange.selectNodeContents(links[i]);
if (linkRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && linkRange.compareBoundaryPoints(range.START_TO_END, range) > -1) {
selectedLinks.push(links[i]);
}
}
}
}
linkRange.detach();
}
} else if (document.selection && document.selection.type != "Control") {
range = document.selection.createRange();
containerEl = range.parentElement();
if (containerEl.nodeName.toLowerCase() == "a") {
selectedLinks.push(containerEl);
} else {
links = containerEl.getElementsByTagName("a");
linkRange = document.body.createTextRange();
for (var i = 0; i < links.length; ++i) {
linkRange.moveToElementText(links[i]);
if (linkRange.compareEndPoints("StartToEnd", range) > -1 && linkRange.compareEndPoints("EndToStart", range) < 1) {
selectedLinks.push(links[i]);
}
}
}
}
return selectedLinks;
}
jsFiddle: http://jsfiddle.net/JRKwH/3/
contentEditable attr set to true makes links in content unclickable
you have to make the link uneditable:
<div class='content-output' contentEditable='true'>
My content and link here: <a contentEditable='false' href='https://www.youtube.com/watch?v=dQw4w9WgXcQ' target='_blank'>Some video</a>
</div>
https://jsfiddle.net/ce13w610/1/
Hyperlink within div tag
Unfortunately what you're trying to do isn't simple. First of all, a clickable hyperlink is something that needs an anchor tag <a>
and by making contenteditable as true, you cant simply type <a>...
, You might want to look into using some libraries such as QuillJS or SlateJS.
The following is an example of QuillJS, if you need to links to be auto-converted, you'd need to use another library that extends Quill's logic
var quill = new Quill('#editor-container', { modules: { toolbar: [ [{ header: [1, 2, false] }], ['bold', 'italic', 'underline'], ['image', 'code-block', 'link'] ] }, placeholder: 'Compose an epic...', theme: 'snow' // or 'bubble'});
#editor-container { height: 375px;}
<link href="//cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet" /><link href="//cdn.quilljs.com/1.3.6/quill.bubble.css" rel="stylesheet" /><link href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css" rel="stylesheet" /><link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/monokai-sublime.min.css" rel="stylesheet" /><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.js"></script><script src="//cdn.quilljs.com/1.3.6/quill.js"></script>
<div id="editor-container"></div>
Adding link inside a DIV that looks like a Text Area
I've attached the same question asked previously. The anchor tag is contenteditable
, and thus not clickable. You could wrap the anchor tag in another div that takes contenteditable
off.
How to make clickable anchor in contentEditable div?
Allowing the user to paste clickable hyperlinks into a text area while editing
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>
Related Topics
How Much Faster Is It to Use Inline/Base64 Images for a Web Site Than Just Linking to The Hard File
Will Targeting Ie8 with Conditional Comments Work
CSS/HTML: What Is The Correct Way to Make Text Italic
Why Certain Doctype Declarations Cause 100%-Height Tables and Divs to Stop Working
Purpose of The Crossorigin Attribute...
Change CSS Font-Family for Separate Options in Select Tag
Stylesheet Taken-Over/Replaced by Chinese Characters
Texture Splatting with Three.Js
How to Change Cursor Color Without Changing Text Color
Body { Overflow-X: Hidden; } Breaks Position: Sticky
Extra Space When Centering Elements Using Flexbox
How to Set Gaps (Gutters) in a Flex Container
Gradient Colors in Internet Explorer
CSS Shape with Inset Curve and Transparent Background