Require
1. When entering @, the matching friend menu pops up
2. When the cursor enters the tag containing "@Friends", the menu will pop up
3. When pressing backspace to delete, if the cursor is front of the tag containing "@Friends", the menu will pop up.
4. Compatible with ie, firefox.
Specific practices
For requirements 1, it is natural to think of binding events to the input box. Here you need to bind mousedown, not mouseup. Because if it is mouseup, using event.preventDefault() cannot prevent the keyboard from entering @. In addition, using return false in event callbacks here will not work.
After binding the mousedown event, you need to insert a custom tag containing "@friends". The input box on Sina Weibo is made with textarea. I can’t know how it is handled internally, so I have to read Baidu Tieba.
You can see that the <span class='at'></span> tag has been inserted into the post bar. This should be convenient for background matching with regular expressions.
Specific
The code copy is as follows:
vm.check_key=function(e){
var editor=$('editor'),range;
if(e.shiftKey&&e.keyCode==50){
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML("<span id='at"+at_index+"' class='at_span'>@</span>");
}else{
document.execCommand("insertHtml", false,"<span id='at"+at_index+"' class='at_span'>@</span>");
}
e.preventDefault();
}
};
This requires insertion at the cursor, so range is used.
Then the menu is displayed, the key is how to position it. My approach is very garbage, which is to add an id to the inserted span and then locate the menu according to the position of the span id. If there is a better way, please let me know.
Specific
The code copy is as follows:
function at_box_show(at){
var at_pos=avalon($(at)).position();
$('at_box').style.left=at_pos.left+'px';
$('at_box').style.top=at_pos.top+16+'px';
$('at_box').style.display='block';
}
var at_index=0,cur_index=0;
avalon.define('editor', function(vm) {
vm.item_click=function(){
$('at'+cur_index).innerHTML="@"+this.innerHTML;
$('at_box').style.display='none';
at_index++;
};
vm.check_key=function(e){
var editor=$('editor'),a=getCharacterPrecedingCaret(editor),range;
if(e.shiftKey&&e.keyCode==50){
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML("<span id='at"+at_index+"' class='at_span'>@</span>");
}else{
document.execCommand("insertHtml", false,"<span id='at"+at_index+"' class='at_span'>@</span>");
}
at_box_show('at'+at_index);
cur_index=at_index;
e.preventDefault();
}
};
});
at_show_box locates at_box according to the newly inserted span id, and then displays the menu. cur_index represents the span id where the cursor is currently located. Set this variable because the user may rewind and change the inserted span, and at_index is constantly increasing, so a variable is also needed here.
The user clicks on the friend item in the menu to trigger the item_click callback. In the callback, add the friend's name to the current span using inserHTML. Then hide the menu, at_index++.
The above is listening shift+@, followed by listening for backspace deletion.
The code copy is as follows:
function getTextBeforeCursor(containerEl) {
var precedingChar = "", sel, range, precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
precedChar = range.cloneContents();
}
} else if ( (sel = document.selection)) {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
precedingChar = precedingRange.htmlText;
}
return precedingChar;
}
The function of getTextBeforeCursor is to get the content before the cursor. Due to compatibility, this function can get DocumentFragment that is all the content before the cursor in a standard browser, and in ie, you can only get text (not node). However, this html string can be converted into DocumentFragment. In avalon, parseHTML can be used to turn the html string into node. You can also get node by using $(html)[0] in jquery.
With this function, you can use lastChild to determine whether the cursor is in the lastChild in the html in front of the cursor, and this lastChild is a span.
Specific
The code copy is as follows:
var a=getTextBeforeCursor($('editor'));
if(e.keyCode==8){
if(!-[1,]){
var b=avalon.parseHTML(a).lastChild;
}else{
var b=a.lastChild;
}
if(b.nodeType==1&&b.nodeName=='SPAN'){
var id=b.id;
cur_index=b.id.substring(2);
at_box_show(b.id);
}else
$('at_box').style.display='none';
}
Finally, the cursor enters the span label and displays the menu. This obviously requires binding the mouse event. Mouseup is bound here, because if mousedown is bound, you need to click the mouse on the span label again before the menu can be displayed. As for the principle, it is similar to the above.
The code copy is as follows:
vm.check_mouse=function(e){
var editor=$('editor'),a=getTextBeforeCursor(editor);
if(!-[1,]){
var b=avalon.parseHTML(getTextBeforeCursor(editor)).lastChild;
}else{
var b=a.lastChild;
}
if(b!=null&&b.nodeType==1&&b.nodeName=='SPAN'){
var id=b.id;
cur_index=b.id.substring(2);
at_box_show(b.id);
}else
$('at_box').style.display='none';
};
Note that if the cursor is in the span, it must be removed, and the at_box must be positioned according to this id, and also reset cur_index.
As for the ajax update menu, I won't do the character matching
Effect
firefox
ie8
ie7
ie6
download
The above is all the content described in this article. I hope it will be helpful for everyone to understand JavaScript.