Josh Josh - 5 months ago 8
jQuery Question

getting jQuery ui autocomplete to work with @mentions only

I've had a look at a few questions, such as http://stackoverflow.com/a/7222592/2332251

I'm still having trouble reconciling it with the code I have.

At the moment the following works perfectly for searching a username as I start typing.

$(function() {
$("#appendedInputButton").autocomplete({
minLength: 2,
source: "searchusers.php"
});
});


The function in searchusers.php outputs the usernames from the database.

As I said, I'm having trouble making other @mention solutions work for me. I've tried copying over other solutions and swapping my details in but nothing seems to work.

So...


  1. What do I need to do to my current autocomplete script to make it load only
    when I initially type the '@' symbol?

  2. I would really like to be able to have multiple @mentions in my posts

  3. (optional) when autocomplete suggests usernames and when I select the username from the list I want it to appear in my post with the @symbol still appended to the front of the username e.g. "hello @john, the @ symbol is still attached to your username"



If you need more info, please comment and I will provide more :)

Edit I'm just really unsure of the syntax to make it work. For example, using the example answer I posted above, I came up with (but it doesn't work):

function split(val) {
return val.split(/@\s*/);
}

function extractLast(term) {
return split(term).pop();
}

function getTags(term, callback) {
$.ajax({
url: "searchusers.php",
data: {
filter: term,
pagesize: 5
},
type: "POST",
success: callback,
jsonp: "jsonp",
dataType: "jsonp"
});
}

$(document).ready(function() {

$("#appendedInputButton")
// don't navigate away from the field on tab when selecting an item
.bind("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB && $(this).data("autocomplete").menu.active) {

event.preventDefault();
}
}).autocomplete({
source: function(request, response) {
if (request.term.indexOf("@") >= 0) {
$("#loading").show();
getTags(extractLast(request.term), function(data) {
response($.map(data.tags, function(el) {
return {
value: el.name,
count: el.count
}
}));
$("#loading").hide();
});
}
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join("");
return false;
}
}).data("autocomplete")._renderItem = function(ul, item) {
return $("<li>")
.data("item.autocomplete", item)
.append("<a>" + item.label + "&nbsp;<span class='count'>(" + item.count + ")</span></a>")
.appendTo(ul);
};
});


Where do I insert searchusers.php, #appendedInputButton and other specific info? I hope this makes sense.

Answer

I will form an answer based on my comments.

First of all lets review the list of requirements:

  • autocomplete usernames started with @ symbol
  • prepend usernames with @ symbol
  • multiple @mentions in a text
  • edit any @mention anywhere in a text

to implement the last requirement we need some magic functions that i found on stackoverflow:

Also to detect a username somewhere in the text we need to define some constraints for usernames. I assume that it can have only letters and numbers and test it with \w+ pattern.

The live demo you can find here http://jsfiddle.net/AU92X/6/ It always returns 2 rows without filtering just to demonstrate the behavior. In the listing below i've put the original getTags function from the question as it looks fine for me. Although i have no idea how searchusers.php works.

function getCaretPosition (elem) {

  // Initialize
  var iCaretPos = 0;

  // IE Support
  if (document.selection) {

    // Set focus on the element
    elem.focus ();

    // To get cursor position, get empty selection range
    var oSel = document.selection.createRange ();

    // Move selection start to 0 position
    oSel.moveStart ('character', -elem.value.length);

    // The caret position is selection length
    iCaretPos = oSel.text.length;
  }
  // Firefox support
  else if (elem.selectionStart || elem.selectionStart == '0')
    iCaretPos = elem.selectionStart;

  // Return results
  return (iCaretPos);
}

function setCaretPosition(elem, caretPos) {
    if(elem != null) {
        if(elem.createTextRange) {
            var range = elem.createTextRange();
            range.move('character', caretPos);
            range.select();
        }
        else {
            if(elem.selectionStart) {
                elem.focus();
                elem.setSelectionRange(caretPos, caretPos);
            }
            else
                elem.focus();
        }
    }
}

function getTags(term, callback) {
    $.ajax({
        url: "searchusers.php",
        data: {
            filter: term,
            pagesize: 5
        },
        type: "POST",
        success: callback,
        jsonp: "jsonp",
        dataType: "jsonp"
   });    
}

$(document).ready(function() {
    $("#appendedInputButton").autocomplete({
        source: function(request, response) {
            var term = request.term;
            var pos = getCaretPosition(this.element.get(0));
            var substr = term.substring(0, pos);
            var lastIndex = substr.lastIndexOf('@');
            if (lastIndex >= 0){
                var username = substr.substr(lastIndex + 1);
                if (username.length && (/^\w+$/g).test(username)){
                    getTags(username, function(data) {
                        response($.map(data.tags, function(el) {
                            return {
                                value: el.name,
                                count: el.count
                            }
                        }));
                    });
                    return;
                }
            }

            response({}); 
        },
        focus: function() {
            // prevent value inserted on focus
            return false;
        },
        select: function(event, ui) {
            var pos = getCaretPosition(this);
            var substr = this.value.substring(0, pos);
            var lastIndex = substr.lastIndexOf('@');
            if (lastIndex >= 0){
                var prependStr = this.value.substring(0, lastIndex);
                this.value = prependStr + '@' + ui.item.value + this.value.substr(pos);
                setCaretPosition(this, prependStr.length + ui.item.value.length + 1);
            }    
            return false;
        }
    }).data("ui-autocomplete")._renderItem = function(ul, item) {
        return $("<li>")
            .data("ui-autocomplete-item", item)
            .append("<a>" + item.label + "&nbsp;<span class='count'>(" + item.count + ")</span></a>")
            .appendTo(ul);
    };
});
Comments