Does anyone have any suggestions on how to edit an <a href=''>
link in a contentEditable div?
It would be ideal once the link is either clicked with mouse, or the cursor hits the link, that the a small prompt would pop up and allow the user to change the href property of the link.
The prompt isn't the issue, but how is it possible to detect the link has been either clicked or that the cursor has arrived at the link?
onfocus
doesn't seem to work in a contentEditable div on Firefox & Safari.
Any ideas?
Does anyone have any suggestions on how to edit an <a href=''>
link in a contentEditable div?
It would be ideal once the link is either clicked with mouse, or the cursor hits the link, that the a small prompt would pop up and allow the user to change the href property of the link.
The prompt isn't the issue, but how is it possible to detect the link has been either clicked or that the cursor has arrived at the link?
onfocus
doesn't seem to work in a contentEditable div on Firefox & Safari.
Any ideas?
Share Improve this question edited Jan 16, 2023 at 11:37 vsync 130k59 gold badges340 silver badges421 bronze badges asked Mar 17, 2010 at 0:35 Travis DTravis D 1211 silver badge5 bronze badges 1 |2 Answers
Reset to default 18I'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());
});
});
I tested it only on chrome
button style
button.cmd {
...
}
toolbar button
<button type="button" id="linkEditor" class="cmd">link</button>
modal dialog -> link editor
<div id="optionDialog" class="modal">
<div class="modal-content">
<div id="linkOption">
<p>
<label>Indirizzo link</label><br />
<input type="text" id="linkUrl" value="" placeholder="http://www..." />
</p>
<p>
<label>Testo link</label><br />
<input type="text" id="linkData" value="" placeholder="link label" />
</p>
<p>
<button type="button" id="linkDone">apply</button>
<button type="button" id="cancel">cancel</button>
</p>
</div>
</div>
</div>
javascript, jquery
var cur_range;
var cur_dialog;
$(document).ready(function() {
$('.editor').focus();
//on toolbar button mousedown keep current range
$('.cmd').mousedown(function(event) {
event.preventDefault();
try {
cur_range = document.getSelection().getRangeAt(0);
}
catch(error) {
console.log(error);
}
});
$('#linkEditor').click(function() {
event.preventDefault();
//if user select edit link, else insert link
if (!cur_range.collapsed) {
//if selection is a link set edit values
if (cur_range.commonAncestorContainer.parentNode.nodeName.toLowerCase() == 'a') {
$('#linkUrl').val(cur_range.commonAncestorContainer.parentNode.href);
$('#linkData').val(cur_range.commonAncestorContainer.data);
}
else {
//alert here
return false;
}
}
else {
$('#linkUrl').val('');
$('#linkData').val('');
}
//open link editor dialog
$('#optionDialog').show();
$('#linkOption').show();
//store current dialog section (link, table, header)
cur_dialog = $('#linkOption');
});
$('#linkDone').click(function() {
event.preventDefault();
if (cur_range.collapsed) {
//insert link at caret position
var node = '<a href="' + $('#linkUrl').val() + '" target="_blank">' + $('#linkData').val() + '</a>';
cur_range.insertNode(cur_range.createContextualFragment(node));
}
else {
//replace existing link values
cur_node = cur_range.commonAncestorContainer.parentNode;
cur_node.href = $('#linkUrl').val();
cur_node.innerText = $('#linkData').val();
}
//update range
document.getSelection().removeAllRanges();
document.getSelection().addRange(cur_range);
document.getSelection().collapseToEnd();
$('.modal-close').click();
//if you don't use observer some code is nedeed
//save_history(1);
});
//When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
optionDialog = document.getElementById('optionDialog');
if (event.target == optionDialog) {
$('#optionDialog').hide();
cur_dialog.hide();
cur_dialog = null;
}
}
});
mousedown
event should be suffice in detecting that. Use a delegated event. – vsync Commented Jan 16, 2023 at 11:39