I'm trying to use typeahead's matcher function to check if my search returns no results. If it returns no results i want to append a div on the end of the search bar. However the matcher function is causing my highlighter to break and return random results. Does anyone know if there is a way to accomplish this without using the matcher function or how use it properly in this instance? I think i might be taking the wrong approach.
$('.shop_search').typeahead({
source: function (query, process) {
map = {};
$.each(data, function (i, data) {
map[data.text] = {
address: data.text2,
name: data.text,
post: data.post
};
shops.push(data.text);
});
process(shops);
shops = [];
},
minLength: 3,
matcher: function (item) {
if (item.indexOf(this.query) == -1) {
$(".dropdown-menu").append($('<li><button class="btn" >Advanced Search</button></li>'));
return true;
}
},
highlighter: function (item) {
var p = map[item];
var itm = ''
+ "<div class='typeahead_primary'>" + p.name + "</div>"
+ "<div class='typeahead_secondary'>" + p.address + </div>"
+ "</div>"
+ "</div>";
return itm;
},
});
Seems to me that you forgot a "
+ "<div class='typeahead_secondary'>" + p.address + </div>"
should be
+ "<div class='typeahead_secondary'>" + p.address + "</div>"
Related
1) I need to find a way to have multiple of id="select-repo" because every single "add another item" is the same. "Add Another Item" is suppose to add another row of html with that id="select-repo" for now it's just a empty textbox.
Generate Functions on the go? Dynamic Functions? I can easily loop the Select box but not the "$('#select-repo').selectize({" function i believe.
2) After number 1 is solved, I need to find a way to know which row of data to update after an option has been selected.
3) Is this easier to get done with VUE.JS, since i'm using laravel , integration should be easier with Vue.JS
What is your advice, I was told to use stuff like ReactJS / styled components? is there anyway to not switch framework to just get this done?
Please Advice.
HTML Code
<td><select id="select-repo" class="repositories"></select></td>
JS Code
<script>
//<select id="select-repo"></select>
$('#select-repo').selectize({
valueField: 'url',
labelField: 'name',
searchField: 'name',
options: [],
create: false,
render: {
option: function(item, escape) {
return '<div>' +
'<span class="title">' +
'<span class="name"><i class="icon ' + (item.fork ? 'fork' : 'source') + '"></i>' + escape(item.name) + '</span>' +
'<span class="by">' + escape(item.username) + '</span>' +
'</span>' +
'<span class="description">' + escape(item.description) + '</span>' +
'<ul class="meta">' +
(item.language ? '<li class="language">' + escape(item.language) + '</li>' : '') +
'<li class="watchers"><span>' + escape(item.watchers) + '</span> watchers</li>' +
'<li class="forks"><span>' + escape(item.forks) + '</span> forks</li>' +
'</ul>' +
'</div>';
}
},
score: function(search) {
var score = this.getScoreFunction(search);
return function(item) {
return score(item) * (1 + Math.min(item.watchers / 100, 1));
};
},
load: function(query, callback) {
if (!query.length) return callback();
$.ajax({
url: 'https://api.github.com/legacy/repos/search/' + encodeURIComponent(query),
type: 'GET',
error: function() {
callback();
},
success: function(res) {
callback(res.repositories.slice(0, 10));
}
});
},
onChange: function(value) {
alert(value);
}
});
</script>
Since id can not be the same, you can define a global variable like index to memo the count as part of id;
Like to dynamically add select with id "select-repo"+index; e.g. select-repo1, select-repo999
Here's an example:
var index = 1;
function addSelect(){
$('#somewhere').append('<select id="select-repo'+index+'">');
$('select-rep'+index).selectize(){
....
};
index++;
}
And you can easily get the select index by parse Id string.
I am coding a simple RSS feed using jquery and a feed from wired. Everything is working great, but for some reason the result is including a NaN after the description. I cannot figure out what it is trying to pull, and since it is not wrapped in any tags, it follows a paragraph as such:
<p></p> NaN </div>
I cannot use css to hide it, and i dont want to limit the description length as some are longer than others and setting an arbitrary character limit may allow it to display anyways on shorter descriptions.
xml feed: http://www.wired.com/category/business/feed/
script:
(function ($) {
$.fn.FeedEk = function (opt) {
var def = $.extend({
FeedUrl: "http://www.wired.com/category/business/feed/",
MaxCount: 5,
ShowDesc: true,
ShowPubDate: true,
TitleLinkTarget: "_blank",
}, opt);
var id = $(this).attr("id");
var i;
$("#" + id).empty().append('<img src="loader.gif" />');
$.ajax({
url: "http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=" + def.MaxCount + "&output=json&q=" + encodeURIComponent(def.FeedUrl) + "&hl=en&callback=?",
dataType: "json",
success: function (data) {
$("#" + id).empty();
var s = "";
$.each(data.responseData.feed.entries, function (e, item) {
s += '<li><div class="itemTitle"><a href="' + item.link + '" target="' + def.TitleLinkTarget + '" >' + item.title + "</a></div>";
if (def.ShowPubDate) {
i = new Date(item.publishedDate);
s += '<div class="itemDate">' + i.toLocaleDateString() + "</div>";
}
if (def.ShowDesc) {
if (def.DescCharacterLimit > 0 && item.content.length > def.DescCharacterLimit) {
var StringStartAfterImage = item.content.indexOf('>',item.content.indexOf('< img')) + 1;
s += '<div class="itemContent">' + item.content.substr(0, def.DescCharacterLimit + StringStartAfterImage) + "..";
}
else {
s += '<div class="itemContent">' + item.content;
}
s += + "</div>";
}
});
$("#" + id).append('<ul class="feedEkList">' + s + "</ul>");
}
});
};
})(jQuery);
$(document).ready(function() {
$('#home-news').FeedEk({
FeedUrl: 'http://www.wired.com/category/business/feed/',
MaxCount: 5,
ShowDesc: true,
ShowPubDate: true,
});
});
html:
<div class="newsCenter">
<div class="news">
<div id="home-news"> </div>
</div>
</div>
any help is much appreciated! Thank you!
i solved this by removing code after the else statement and closing the div in the else statement, i do not need the data that is not generating so this solution works for me.
I have a strange problem with my script. I am getting a JSON result set and want to iterate it and then display in a div. I checked fiddler and I can see the entire set being returned like the set below
[{"EPubID":71,"SerialID":1,"PartnerID":343,"Partner":"Aberdeen, City of ","PublicationTitle":"Uploading multiple files test","AuthFirstName":null,"AuthMiddleName":null,"AuthLastName":null,"AuthFullName":null,"PublicationYear":2011,"SubmitterEmail":null,"VolumeNumber":null,"Issue":null,"AlreadyInCatalog":false,"InCatalog":"No","Status":"D","Notes":"testing multiple file uploads","IsMonograph":false,"Monographed":"No","SubmittedDate":"\/Date(1317913458810)\/","SubmittedBy":"admin","ApprovedDate":"\/Date(1317914842263)\/","ApprovedBy":"admin","SubmittingPartnerID":0,"OriginalRefId":"343-71","SerialName":"None","URL":null,"InfoRecordID":0,"LastModified":"\/Date(-62135568000000)\/","IsSerial":false,"Approved":false,"Delete":false,"Pending":false,"files":null},{"EPubID":72,"SerialID":19,"PartnerID":26,"Partner":"Digital Archives","PublicationTitle":"testing multiple file uploads ","AuthFirstName":null,"AuthMiddleName":null,"AuthLastName":null,"AuthFullName":null,"PublicationYear":2001,"SubmitterEmail":null,"VolumeNumber":"1","Issue":"1","AlreadyInCatalog":false,"InCatalog":"No","Status":"A","Notes":"this should work","IsMonograph":false,"Monographed":"No","SubmittedDate":"\/Date(1317915134767)\/","SubmittedBy":"admin","ApprovedDate":"\/Date(1317915430627)\/","ApprovedBy":"admin","SubmittingPartnerID":0,"OriginalRefId":"26-72","SerialName":"Fake Test Serial","URL":null,"InfoRecordID":0,"LastModified":"\/Date(-62135568000000)\/","IsSerial":false,"Approved":false,"Delete":false,"Pending":false,"files":null}]
The problem is my script is only displaying the first item returned and nothing else. Here is my script.
function SearchExistingEpubs() {
var title = $("input#PublicationTitle").val();
$('#Results').hide();
$("div#SearchResults").innerHTML = '';
$.getJSON('/EPub/SearchExistingEpubs/' + title, null, function (data) {
var items = [];
var found = false;
$.each(data, function (key, val) {
found = true;
$("div#SearchResults").empty();
$("div#SearchResults").append("Title: " + val.PublicationTitle + " Owning Partner: " + val.Partner + " Year: " + val.PublicationYear) ;
$('#Results').show();
});
if (!found) {
$("div#SearchResults").empty();
//$("div#SearchResults").html('');
$("div#SearchResults").append("No documents found");
$('#Results').show();
//$('#Results').slideUp(10000);
$('#Results').animate({height:'toggle'},10000);
//$('#Results').fadeOut(10000);
}
//$('#Results').show();
});
};
You're wiping out the contents of the div in each iteration of the loop with your call to empty():
$.each(data, function (key, val) {
found = true;
$("div#SearchResults").empty(); // <------ REMOVE this line
$("div#SearchResults").append("Title: " + val.PublicationTitle + " Owning Partner: " + val.Partner + " Year: " + val.PublicationYear) ;
$('#Results').show();
});
But doing dom updates in a loop is not usually a good idea. Why not build up your string and do one dom update:
var content = '';
$.each(data, function (key, val) {
found = true;
content += "Title: " + val.PublicationTitle + " Owning Partner: " + val.Partner + " Year: " + val.PublicationYear;
});
$("div#SearchResults").append(content);
$('#Results').show();
In your .each loop you're calling $("div#SearchResults").empty(); this will clear any content you've previously appended to this div.
Try the following:
function SearchExistingEpubs() {
var title = $("input#PublicationTitle").val();
$('#Results').hide();
$("div#SearchResults").empty();
$.getJSON('/EPub/SearchExistingEpubs/' + title, null, function (data) {
$("div#SearchResults").empty();
var items = [];
if (data.length) {
$.each(data, function (key, val) {
$("div#SearchResults").append("Title: " + val.PublicationTitle + " Owning Partner: " + val.Partner + " Year: " + val.PublicationYear);
});
$('#Results').show();
} else {
$("div#SearchResults").append("No documents found");
$('#Results').show();
$('#Results').animate({height:'toggle'},10000);
}
});
};
Buildgames returns rows:
<a>....</a>
<a>....</a>
When I click on each a the Buildcar_s function returns all the data inside an alert.
Instead of this alert I want to put all the results in a div under each a, so it would look like:
<a>.....clicked ...</a>
<div>....
...
</div>
<a>....not clicked...</a>
<a>....not clicked...</a>
<a>....not clicked...</a>
How can we put a div only under the a which was clicked?
function Buildcar_s(items) {
var div = $('<div/>');
$.each(items, function() {
var car_ = this.car_;
$('<a>' + this.car_ + '----' + this.Names + '---' + '</a>').click(function() {
_Services.invoke({
method: 'GetgamesRows',
data: {
car_Number: car_
},
success: function(car_s) {
var div = Buildgames(car_s);
$(div).insertAfter($a);
}
});
}).appendTo(div);
$('<br/>').appendTo(div);
});
$("#leftRows").append(div);
}
function Buildgames(items) {
var place = '<div>';
$.each(items, function() {
place += 'mmmmmm<br/>';
});
place += '</div>';
return place;
}
Try this, relevant changes have been commented:
function Buildcar_s(items) {
var div = $('<div/>');
$.each(items, function() {
var car_ = this.car_;
$('<a>' + this.car_ + '----' + this.Names + '---' + '</a>').click(function() {
var $a = this;
_Services.invoke({
method: 'GetgamesRows',
data: {
car_Number: car_
},
success: function(car_s) {
var div = Buildgames(car_s);
// this inserts the HTML generated from the function,
// under the A element which was clicked on.
$(div).insertAfter($a);
}
});
}).appendTo(div);
$('<br/>').appendTo(div);
});
$("#leftRows").append(div);
}
function Buildgames(items) {
var place = '<div>';
$.each(items, function() {
place += '<div style="float: right;"> ' + this.CITY + ' ' + '</div><BR />' + +'<br/><br/>';
});
place += '</div>';
return place; // returns the string created, as opposed to alerting it.
}
Is there a better, more concise way to do this:
function getTweets(){
$.getJSON("http://search.twitter.com/search.json?callback=?&q=superfad",
function(data){
tweetsLoaded = true;
$.each(data.results, function(i,item){
var textPlain = item.text;
var textLinked = linkify(textPlain);
var textHashed = hashify(textLinked);
var textListed = listify(textHashed);
function linkify(tweet){
return tweet.replace(/(http:\/\/[^\s]*)/g, "<a class='twtr-link' target=\"_blank\" href=\"$1\">$1</a>");
}
function hashify(tweet){
return tweet.replace(/(^|\s+)#(\w+)/gi, function(m, before, hash) {
return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
});
}
function listify(tweet) {
return tweet.replace(/\B[#@]([a-zA-Z0-9_]{1,20})/g, function(m, username) {
return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">#' + username + '</a>';
});
}
$("#twitter_results").append('<li class="twitter"><img class="twitter_img" src="' + item.profile_image_url + '"/>'+ textListed + '</li>');
});
});
} //end getTweets
// define this globally
function stuffify(match, group1, group2) {
switch (group1 || group2) {
case 'http':
return '<a class="twtr-link" target="_blank" href="' + match + '">' + match + '</a>';
case '#':
return '<a class="twtr-hashtag" target="_blank" href="http://twitter.com/search?q=' + encodeURIComponent(match) + '">#' + match + '</a>'
case '#':
return '<a class="twtr-atreply" target="_blank" href="http://twitter.com/intent/user?screen_name=' + encodeURIComponent(match) + '">#' + match + '</a>';
default:
return match;
}
}
function(data){
tweetsLoaded = true;
var interestingParts = /(http):\/\/\S+|(#|#)[^\s.,!?;^()\[\]<>{}]+/g;
$.each(data.results, function(i,item) {
var newText = item.text.replace(interestingParts, stuffify);
$("#twitter_results").append('<li class="twitter"><img class="twitter_img" src="' + item.profile_image_url + '"/>'+ newText + '</li>');
});
}
I would realign the code such that the text manipulation functions (linkify, hashify, listify) are not inside of the each function, nor are they even inside of getTweets itself. If it's in getTweets, then every time you call that function they have to be redefined. Worse, inside of the each where you had them, those functions are redefined for every item in the returned tweet-set.
Also, there is no need to store the return of each of those functions in it's own var since you only use them once afterward and aren't performing any checks before using them. Just nest the function calls.
Finally, since you're calling append over and over on the same element (inside the each) I pre-queried that element instead of querying for it every time the each function is executed
Those changes, along with some things I do for my own performance preferences are exhibited in the code sample pasted below.
There are other things I would do, however I won't show them here--you can see it in my posted answer at Trouble Converting jQuery Script to Plugin -- a question about making a jQuery plugin which does the same ting as your code). You should store the formatted tweets in an array rather than append each as you get it. Having built that array, you should then combine it into one string and call append once with that string. Making this a jQuery plugin would also be nice for you as it would not require you modify the code to change the targeted DOM element. Looking into use of String.prototype.link would be good as well.
(demo: http://jsfiddle.net/JAAulde/fQ3Lp/2/ )
var getTweets = ( function()
{
/* Privatized text manipulation functions */
var linkify = function( tweet )
{
return tweet.replace( /(http:\/\/[^\s]*)/g, "<a class='twtr-link' target=\"_blank\" href=\"$1\">$1</a>" );
};
var hashify = function( tweet )
{
return tweet.replace( /(^|\s+)#(\w+)/gi, function(m, before, hash)
{
return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
} );
};
var listify = function( tweet )
{
return tweet.replace(/\B[#@]([a-zA-Z0-9_]{1,20})/g, function(m, username)
{
return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">#' + username + '</a>';
} );
};
var $twitterResultTarget = $( "#twitter_results" );
/* The actual function which is stored in `getTweets` */
return function()
{
$.getJSON( "http://search.twitter.com/search.json?callback=?&q=superfad", function( data )
{
tweetsLoaded = true;
$.each( data.results, function( i, item )
{
$twitterResultTarget
.append( [
'<li class="twitter"><img class="twitter_img" src="',
item.profile_image_url,
'"/>',
listify( hashify( linkify( item.text ) ) ),
'</li>'
].join( '' ) );
} );
} );
}
}() );
Try to avoid using anonymous functions, its always less confusing if they all have names and are declared outside of any other functions.
I'd probably write in in the jQuery plugin style, and use split as opposed to replace with HTML in callbacks:
(function($){
function getTweets(q) {
var
$set = this,
prefixes = {
'h': 'h',
'#': 'http://twitter.com/search?q=%23',
'#': 'http://twitter.com/intent/user?screen_name='
},
classes = {
'h': 'twtr-link',
'#': 'twtr-hashtag',
'#': 'twtr-atreply'
};
$.getJSON(
"http://search.twitter.com/search.json?callback=?&q=" + encodeURIComponent(q),
function(data){
$.each(data.results, function(i, item){
var
$li = $('<li class="twitter"></li>')
.append('<img class="twitter_img" src="' + item.profile_image_url + '"/> ');
$.each(
item.text.split(
/(\s+)|(https?:\/\/[^\s]*)|(#\w+)|(#[a-zA-Z0-9_]{1,20})/g
),
function(i, chunk) {
if (/^(https?:\/\/|#|#)/.test(chunk)) {
$('<a target="_blank"></a>')
.addClass(classes[chunk[0]])
.attr('href', prefixes[chunk[0]] + chunk.substr(1))
.text(chunk)
.appendTo($li);
} else if (chunk) {
$li.append(document.createTextNode(chunk));
}
}
);
$li.appendTo($set);
});
}
);
}
$.fn.getTweets = getTweets;
})(jQuery);
Then call it like this:
$('#twitter_results').getTweets('superfad');
My $0.02