问题
The idea is this - There is a contenteditable element with some text in it. Am trying to build out a tagging mechanism (kind of like twitter's people tagging when you type '@'). Whenever a user types '@', it shows up a popover with suggestions and filters when they continue typing. Until here it's easy and I have got it figured out. The problem comes when I need to show the popover if/only if the caret is over the element containing the tag.
<div contenteditable="">
<p>Some random text before
<a href="javascript:;"
class="name-suggest"
style="color:inherit !important;text-decoration:inherit !important">@samadams</a>
Some random text after</p>
</div>
Now, whenever the user moves the caret over the a tag / clicks on it, I want to trigger an event that shows the popover, and remove it whenever the caret leaves the a tag. (kind of like focus / blur but they don't seem to work). onmousedown works but there is no way to tell if the cursor has been moved into the anchor tag with the keyboard.
Also, am doing this in angularjs, so, any solution targeted towards that would be preferable but not necessary.
Have been trying to get this to work for a day and any help is greatly appreciated.
回答1:
This will let you know when your caret position is in an anchor node containing an @
$('#content').on('mouseup keydown keyup', function (event) {
var sel = getSelection();
if (sel.type === "Caret") {
var anchorNodeVal = sel.anchorNode.nodeValue;
if ( anchorNodeVal.indexOf('@') >= 0) {
$('#pop').show()
} else {
$('#pop').hide()
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="content" contenteditable="">
<p>Some random text before
<a href="javascript:;"
class="name-suggest"
style="color:inherit !important;text-decoration:inherit !important">@samadams</a>
Some random text after</p>
</div>
<div id="pop" style="display:none">Twitter node found</div>
You could add some regex to further validate the selection.
回答2:
There is a weird move with RegExps and offset calculation in the code below, but let me explain why it's a better solution.
I've been building a complicated editor using contenteditable about a year ago. It wasn't just a disaster. It was a fucking disaster. There is no cover-all-the-cases spec. Browsers behave differently in every possible detail and it changes frequently. Put a caret before @
char and you will get this is Gecko:
<a href="#">|@name
And this in WebKit:
|<a href="#">@name
Well, unless <a>
is paragraph's first child. Then result would be the same as in Gecko. Try to put caret after the nickname and both will tell it's inside the link. Start typing, and caret will pop out the element - a year ago Gecko wasn't doing it.
I've used native Selection & Range APIs in this example, they are IE9+. You may want to use Rangy instead.
$el = $('#content');
var showTip = function (nickname) {
// ...
console.log('Show: ' + nickname);
};
var dismissTip = function () {
// ...
console.log('Hide');
};
// I'm sure there is a better RegExp for this :)
var nicknameRegexp = /(^|\b|\s)\@(\w+)(\s|\b|$)/g;
var trackSelection = function () {
var selection = window.getSelection(),
range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
if (range == null || $el[0].contains(range.commonAncestorContainer) == false) {
return dismissTip();
}
var comparer = range.cloneRange();
comparer.setStart($el[0], 0);
var offset = comparer.toString().length;
var match, from, to;
while (match = nicknameRegexp.exec($el[0].textContent)) {
from = match.index + match[1].length;
to = match.index + match[1].length + match[2].length + 1;
if (offset >= from && offset <= to) {
// Force rewind, otherwise next time result might be incorrect
nicknameRegexp.lastIndex = 0;
return showTip(match[2]);
}
}
return dismissTip();
};
$el.on({
// `mousedown` can happen outside #content
'mousedown': function (e) {
$(document).one('mouseup', function (e) {
// Calling function without a tiny delay will lead to a wrong selection info
setTimeout(trackSelection, 5);
});
},
'keyup': trackSelection
});
来源:https://stackoverflow.com/questions/27004757/firing-an-event-when-the-caret-gets-within-a-particular-div-span-a-tag-and-also