jQuery dynamic search results from an array - javascript

I have a created dynamic search that takes data from an input form and searches through an array of objects for matches. These matches (if they exist) are displayed on one line of a result along with a 'button' to click to take action on that specific search result line.
Each object in the array corresponds to a line of the result assuming that the search term if found in that object.
I have created a delegated listener to listen for clicks on the button for each shown result line. How do I get the button to act on the the 'index: number' contained in the object that created that line of the results.
My code so far is an follows:
var multiArr = [
{label: "Asap Rocky", song: "National", index: 0},
{label: "Rihanna", song: "Umbrella", index: 1},
{label: "Coldplay", song: "Paradise", index: 2}];
$('#results').on("click", ".result-button", function(event) {
alert("You clicked:" + event.target.nodeName);
console.dir($(this).text);
});
$("#finder").keyup(function(e){
var q = $("#finder").val();
console.log("Search Bar contents: " + q);
var pattern = new RegExp( "^" + q, "i");
$("#results").empty();
for (var i = 0; i < multiArr.length; i++){
console.log("Checking for " + q + " in " + multiArr[i].label);
console.log("The above is " + pattern.test(multiArr[i].label));
if (pattern.test(multiArr[i].label)) {
console.log("something in here beings with " + q);
/*console.log("Img Url: " + thaArr);*/
//do something
$("#results").append("<div class='result-line'><div class='result-img'><img src='http://img.youtube.com/vi/36wDeLKKoXE/default.jpg' class='center-result-img'></div><div class='result-song-info'><div class='result-song-title'><p>" + multiArr[i].song + "</p></div><div class='result-song-artist'><p>" + multiArr[i].label + "</p></div></div><div class='result-button'>>></div></div>");
}
}
});

A simple solution would be to first attach a data attribute to the result-button itself and then fetch that data on your click callback, where the data you are attaching is the index value of your element.
In order words, you would add to the result-button div where you append to #results, that is, this:
"<div class='result-button'>"
the following data attribute:
"<div class='result-button' data-id='" + multiArr[i].index + "'>"
Then, in your click call back, you can simply get that id by using this:
$(this).data("id")
Now, just in case this doesn't make total since, I decided to help you out a little bit more by improving your code some. The main improvement I made is that my example doesn't re-create the html on each filter change. Instead, all of the data is appended to the page at the start, and then it is either hidden or shown depending on the label text. Additionally, I changed the way you are building up the elements to make it easier to edit in the future.
JSFiddle: http://jsfiddle.net/seibert_cody/j4rqdh8L/
HTML:
<input id="finder" type="text">
<div id="results"></div>
JS:
$(document).ready(function(){
// Set up the test data
var multiArr = [
{
label: "Asap Rocky",
song: "National",
index: 0
},
{
label: "Rihanna",
song: "Umbrella",
index: 1
},
{
label: "Coldplay",
song: "Paradise",
index: 2
}
];
// Append it all to the #results page initially
for (var i = 0; i < multiArr.length; i++){
$("#results").append(
$("<div class='result-line'></div>").append(
$("<div class='result-img'></div>").append(
$("<img src='http://img.youtube.com/vi/36wDeLKKoXE/default.jpg' class='center-result-img'>")
).append(
$("<div class='result-song-info'></div>").append(
$("<div class='result-song-title'></div>").append(
$("<p>" + multiArr[i].song + "</p>")
)
)
).append(
$("<div class='result-song-artist'></div>").append(
$("<p>" + multiArr[i].label + "</p>")
)
).append(
$("<div class='result-button' data-id='" + multiArr[i].index + "'>Play</div>")
)
)
);
}
// Clicked on the play button
$('#results').on("click", ".result-button", function(event){
console.log($(this).data("id"));
});
// Only show the labels matching filter
$("#finder").keyup(function(e){
var q = $("#finder").val();
var pattern = new RegExp( "^" + q, "i");
$("#results").children().each(function(child){
var label = $(this).find(".result-song-artist p").html();
if (pattern.test(label)){
$(this).show();
}else{
$(this).hide();
}
});
});
});

Related

jQuery, Passing object on click from array to another function

I have a list of objects coming into my web page. One property of the object is firstName. I display some of the properties and have a clickable one that calls another fuction. I want to pass the object from the array to the second function
jQuery.each(x, function () {
$('#results').append("<a href='javascript:void(0)' onClick='workSup(" + '"' + $(this) + '"' + ")' >" + decodeURI(this.firstName) + ' ' + decodeURI(this.lastName) + " - " + decodeURI(this.preApprovalSupervisorName) + " <a/><br/> ");
});
function workSup(x) {
alert(x.firstName);
}
I've tried passing this as well as $(this). The alert reads "undefined"
What am I doing wrong? Thanks!
jQuery has a great function, .data(), I'd recommend you look into:
$('#selector').data("key", "value (string, number, object, etc.)")
$('#selector').data("key"); //Returns the data, or null if not yet set
Also, I highly recommend moving away from onclick to
$('.myClassName').on("eventName (such as click, change or keyup)", "jqSelector", handlerFunction);
var x = [{firstName:"John", lastName:"Lennon"}, {firstName:"Phil", lastName:"Ochs"}]
$(document).ready(function() {
jQuery.each(x, function () {
var linkEl = $('<a class="myLink" href="javascript:void(0)">' + this.firstName + " " + this.lastName + '</a>');
linkEl.data("myObjData", this);
$('#results').append(linkEl);
});
$(document).on("click", ".myLink", function() {
var myObjData = $(this).data("myObjData");
//myObjData.firstName can be accessed here
alert(JSON.stringify(myObjData));
});
});
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="results"></div>
</body>
</html>
$('.myClass').on() is even better than $('.myClass').click() because it will even listen to elements that are dynamically added later
jQuery.each(x, function() {
//create the a element
var $a = $("<a href='javascript:void(0)'>"+
decodeURI(this.firstName) +' '+ decodeURI(this.lastName) +
" - "+ decodeURI(this.preApprovalSupervisorName) +
"</a><br/>");
//bind the event handler to it
$a.on('click', function(){
workSup(x);
});
});
As mentioned by #taplar you're creating inline binding. Basically what happens is that your $(this) gets resolved as string and later inside your workSup() method you're trying to access firstName property of the string which is obviously undefined.
What you have to do instead is to dynamically create your link and attach event listener to it (in which you call the workSup() method) like so:
// Your data
const x = [
{ firstName: 'First', lastName: 'Last', preApprovalSupervisorName: 'Super' },
{ firstName: 'Another', lastName: 'One', preApprovalSupervisorName: 'His super' },
];
// Your workSup method
const workSup = x => alert(x.firstName);
// Loop though each item
$.each(x, function() {
// Create new <a> element
const $element = $('<a></a>')
// Add href attribute
.attr('href', 'javascript:void(0)')
// Set its text
.text(`${decodeURI(this.firstName)} ${decodeURI(this.lastName)} ${decodeURI(this.preApprovalSupervisorName)}`)
// Attach onClick listener
.on('click', () => workSup(this));
// Append the element to the results and add line break
$('#results')
.append($element)
.append('<br />');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="results"></div>
var x =[{firstName: "John", lastName:"DG", preApprovalSupervisorName: "sup"}];
jQuery.each(x, function (index, value) {
$('#results').append(
$("<a>")
.attr("href","#")
.html( decodeURI(value.firstName) + ' ' + decodeURI(value.lastName) + " - " + decodeURI(value.preApprovalSupervisorName))
.click(function(){
workSup(value);
})
);
});
function workSup(x) {
alert(x.firstName);
}

Find all Divs after being created with JQuery on click event

I'm trying to find all divs that have been created from my click event and split them into another div (.wrapAll) on a count of 3. I can't seem to get anything back when i console.log the vars length. I know this works when I do that same process on the html thats been statically typed. Below is my code and thank you fo the thoughts!
jQuery(document).ready(function($) {
// load default twitch channels
$.getJSON('https://api.twitch.tv/kraken/streams/freecodecamp?callback=?', function(data) {
//console.log(data);
});
// Bind 'Enter' to click event
$(document).bind('keypress', function(e) {
if (e.keyCode == 13) {
$('#search').trigger('click');
}
});
// manually search for games
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON( "https://api.twitch.tv/kraken/search/streams?q=" + search + "", function(data2) {
// console.log(data2.streams.length);
data2.streams.forEach(function(entry) {
//console.log(entry._links);
var streamURL = entry.channel.url;
url = entry.preview.medium;
$('#results').append('<div class="searchResults"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game + '<br> Viewers: ' + entry.viewers +'<br> Is Mature: ' + entry.channel.mature + '<br> Status: ' + entry.channel.status + ' </p></div><hr>');
});
});
// Get 3 divs and slice into one div to style ** problem child **
var a = $('div[id^=searchResu]').find('div');
console.log(a.length);
for( var i = 0; i < a.length; i+=3 ) {
a.slice(i, i+3).wrapAll('<div class="slide"></div>');
}
});
});
Check out this plunker here. I believe this does what your looking for.
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery#2.1.4" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<script>
jQuery(document).ready(function($) {
function appendHtmlContent(resultHtmlContent) {
resultHtmlContent = '<div class="slide">' + resultHtmlContent + '</div>';
$('#results').append(resultHtmlContent);
}
function processSvcResponse(data2) {
var count = 0,
searchResultContents = '',
$div = $("<div>", { class: "searchResults"});
data2.streams.forEach(function(entry) {
var streamURL = entry.channel.url;
url = entry.preview.medium;
searchResultContents += '<div class="searchResults"><a href="' + streamURL
+ '" target="_blank"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game
+ '<br> Viewers: ' + entry.viewers + '<br> Is Mature: ' + entry.channel.mature
+ '<br> Status: ' + entry.channel.status + ' </p></a></div><hr>';
count++;
if(count === 3) {
appendHtmlContent(searchResultContents);
searchResultContents = '';
count = 0;
}
});
// more results that have not been appended?
if(searchResultContents) {
appendHtmlContent(searchResultContents);
}
}
// load default twitch channels
$.getJSON('https://api.twitch.tv/kraken/streams/freecodecamp?callback=?', function(data) {});
// Bind 'Enter' to click event
$(document).bind('keypress', function(e) {
if (e.keyCode == 13) {
$('#search').trigger('click');
}
});
// manually search for games
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON("https://api.twitch.tv/kraken/search/streams?q=" + search, processSvcResponse);
});
});
</script>
</head>
<body>
<input id="searchTerm" type="text" />
<button id="search" type="button">Search</button>
<div id="results"></div>
</body>
</html>
If I understand correctly you are wanting to iterate over the results and for every third one wrap it inside a div with class "slider". As mentioned in the comments by #charlietfl in order to query newly created DOM elements using jQuery you have to query them after they are created. In the call to jQuery.getJSON the second argument accepts a callback function. The signature is jQuery.getJSON(url, someCallbackFunction). In order to make your code a bit more readable I moved "function(data2)" up and named it processSvcResponse. Inside processSvcResponse I build up an HTML string from the results and track how many results are processed by using a counter variable. Once the counter reaches 3 I append the contents to the results div and reset the counter. This solution does not "find" the divs and slice/wrapAll as you were intending to do originally, however, I believe this still accomplishes your goal.
As #charlietfl said, you'll need to place the code that wraps your divs in the callback for getJSON. Your click event listener would look something like this then:
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON( "https://api.twitch.tv/kraken/search/streams?q=" + search + "", function(data2) {
// console.log(data2.streams.length);
data2.streams.forEach(function(entry) {
//console.log(entry._links);
var streamURL = entry.channel.url;
url = entry.preview.medium;
$('#results').append('<div class="searchResults"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game + '<br> Viewers: ' + entry.viewers +'<br> Is Mature: ' + entry.channel.mature + '<br> Status: ' + entry.channel.status + ' </p></div><hr>');
});
// Get 3 divs and slice into one div to style ** problem child **
var a = $('div[id^=searchResu]').find('div');
console.log(a.length);
for( var i = 0; i < a.length; i+=3 ) {
a.slice(i, i+3).wrapAll('<div class="slide"></div>');
}
});
});

jQuery getJSON and get value from button

I'm download data from JSON file and display button with value:
function iterateOverPrzepisy(best) {
$('#listaPrzepisow').html('');
$.getJSON('przepisy.json', function(data) {
for (var x in przepisyDost) {
$('#listaPrzepisow').append(" <div data-role=\"collapsible\"><h2>" + przepisyDost[x].nazwa + "</h2>" +
"<ul data-role=\"listview\" data-theme=\"d\" data-divider-theme=\"d\">" +
"<li>" +
"<h3>Składniki: " + przepisyDost[x].skladniki + "</h3>" +
"<p class='ui-li-desc' style='white-space: pre-wrap; text-align: justify;'>" + przepisyDost[x].tresc + "</p>" +
"<button id='ulubioneBtn' value='" + przepisyDost[x].id + "'>Ulubione</button></li>" +
"</ul>" +
"</div>");
j++;
}
})
}
When I click to button #ulubioneBtn I would like to get value from this button. So I add done to getJSON
}).done(function(data){
$('button#ulubioneBtn').click(function (event) {
console.log("Ulubione: ");
event.preventDefault();
var id = $("button#ulubioneBtn").val();
console.log("Value: " + id);
//dodajemy do ulubionych
localStorage.setItem("ulubione"+id, id);
});
});
But it's not working. When I click on button Ulubione I always get in console log value = 0
The problem seems to be that you add multiple buttons with the same id. An id of a html element should be unique.
przepisyDost does not appear to be defined at
for (var x in przepisyDost) {
? Try
for (var x in data.przepisyDost) {
Duplicate id's are appended to document at
"<button id='ulubioneBtn' value='" + przepisyDost[x].id
+ "'>Ulubione</button></li>" +
within for loop. Try substituting class for id when appending html string to document
"<button class='ulubioneBtn' value='" + data.przepisyDost[x].id
+ "'>Ulubione</button></li>" +
You could use event delegation to attach click event to .ulubioneBtn elements, outside of .done()
$("#listaPrzepisow").on("click", ".ulubioneBtn", function() {
// do stuff
})
I have created a dummy JSON and executed the same JS with a single change.
In onclick handler instead of getting button I am using $(event.target).
And it is working fine.
Please find the fiddle https://jsfiddle.net/85sctcn9/
$('button#ulubioneBtn').click(function (event) {
console.log("Ulubione: ");
event.preventDefault();
var id = $(event.target).val();
console.log("Value: " + id);
//dodajemy do ulubionych
localStorage.setItem("ulubione"+id, id);
});
Seems like first object doesn't have any id value.
Please check JSON response returned from server.
Hope this helps you in solving.

How to get element data within click function?

Is there a tidier/easier way of getting the checkbox element's 'catNum' value when in its click() function?
function createCategoriesList() {
var catNames = new Array("First cat", "Second cat", "Third cat");
var catImageURLs = new Array("First.png", "Second.png", "Third.png");
jQuery('<ul/>', {
id: 'map-cats'
}).appendTo('#map-controls');
for(var i = 0; i < catNames.length; i++ ) {
var listItem = jQuery('<li/>').appendTo('#map-cats');
jQuery('<img/>', {
src: catImageURLs[i],
alt: ''
}).appendTo(listItem);
var checkbox = jQuery('<input/>', {
type: 'checkbox',
checked: 'checked',
id: 'cat_' + i,
name: 'cat_' + i
}).appendTo(listItem);
checkbox.data("catNum", i);
checkbox.click(function() {
//alert("The cat num selected is: " + this.data("catNum")); //throws exception
alert("The cat num selected is: " + jQuery('#' + this.id).data('catNum')); //works but there must be a better way??
});
jQuery('<label/>', {
htmlFor: catImageURLs[i],
text: catNames[i],
for: 'cat_' + i
}).appendTo(listItem);
}
}
The element that comes into the click function is a raw DOM object, not a jQuery object, so in order to use jQuery methods on it, we have to pass it through jQuery first. This is why this.data("catNum") doesn't work in your example code.
But the jQuery() function can accept DOM objects as well as selector strings, so instead of
jQuery('#' + this.id)
you can just use
jQuery(this)
Which as you wanted, a lot tidier.
Alternatively, you can use this as a raw DOM object, but you'd need to use the DOM methods with it rather than the jQuery methods:
this.dataset.catNum //for modern HTML5 browsers.
or
this.getAttribute("data-catNum") //works in all browsers
checkbox.click(function() {
alert("The cat num selected is: " + jQuery(this).data('catNum'));
});
My two cents. Sorry for more extensive answer than asked / I planned. Might have typos but seems to work at js fiddle:
$(function(){
createCategoriesList();
});
function createCategoriesList() {
var catNames = new Array("First cat", "Second cat", "Third cat");
var catImageURLs = new Array("First.png", "Second.png", "Third.png");
var output = '<ul id="map-cats">';
$.each(catNames, function(i, v) {
output += '<li>';
output += '<label for="cat_'+i+'">'+v+'</label>';
output += '<input type="checkbox" id="cat_'+i+'" name="cat_'+i+'" data-catnum="'+i+'" />';
output += '<img src="'+catImageURLs[i]+'" alt="" title="" />';
output += '</li>';
});
output += '</ul>';
$("#map-controls").html(output);
$("#map-cats").on("click", "input", function(){
alert($(this).data("catnum"));
});
}

jQuery Array with Data tags and print to table

I have an unordered list of items with values appended as Data tags to each of the list items. When the user selects an item from the list, the li fades, the Data tags are printed into a table as separate cells, and all data is stored into an array. I was able to append each item to another list, but I am having difficult adding as a table row.
I believe it is the use of 'append' but, the li just gets added within table and with the current order of adding to the array, the data gets compounded.
I feel that the order needs to be click > add to array > then print to table.
Here is a link to my most recent.
Thank you for your assistance in advance.
var myArraySelected = [] ;
$(function() {
var myFunc = function() {
myArraySelected = [] ;
var userList = $('#selected');
userList.children('li').each(function() {
var userID = $(this).attr('data-user');
var userService = $(this).attr('data-role');
var userCategory = $(this).attr('data-category');
var userName = $(this).attr('data-name');
myArraySelected.push({
'id':userID,
'name':userName,
'role':userService,
'category' :userCategory
});
});
};
$('#userList li').click(function() {
$(this).fadeOut('slow', function() { // code to run after the fadeOut
$(this).append('<tr><td><p>'+ $(this).attr('data-name') + '<br>' + $(this).attr('data-user') + '<br>' + $(this).attr('data-role') + '<br>' + $(this).attr('data-category') + '</p></td></tr>')
$(this).appendTo('#selected').fadeIn('slow');
myFunc();
});
});
myFunc();
});
From your question I am assuming that you are trying to append the clicked list-item's data to a table element. Currently you are appending to the list-item and then appending the list item to a table. How about just appending the table row onto the table without using the list-item.
Change:
$(this).append('<tr><td><p>'+ $(this).attr('data-name') + '<br>' + $(this).attr('data-user') + '<br>' + $(this).attr('data-role') + '<br>' + $(this).attr('data-category') + '</p></td></tr>')
$(this).appendTo('#selected').fadeIn('slow');
To:
$('#selected').append('<tr><td><p>'+ $(this).attr('data-name') + '<br>' + $(this).attr('data-user') + '<br>' + $(this).attr('data-role') + '<br>' + $(this).attr('data-category') + '</p></td></tr>').find('tr:last').fadeIn();
And then to add to your array all you need to do is push another index onto the array right in the same place in your code:
$('#userList li').click(function() {
//cache the `$(this)` selector since it will be used more than once
var $this = $(this);
$this.fadeOut('slow', function() { // code to run after the fadeOut
//append a row onto the `#selected` table using the clicked list-item's data-attributes as data; notice after the append I select the newly appended table row and fade it in (which assumes that it's CSS is making it hidden at first)
$('#selected').append('<tr><td><p>'+ $this.attr('data-name') + '<br>' + $this.attr('data-user') + '<br>' + $this.attr('data-role') + '<br>' + $this.attr('data-category') + '</p></td></tr>').find('tr:last').fadeIn();
//now push a new index onto the `MyArraySelceted` array using the data-attributes for the clicked list-item as data
myArraySelected.push({
'id' : $this.attr('data-user'),
'name' : $this.attr('data-name'),
'role' : $this.attr('data-role'),
'category' : $this.attr('data-category')
});
//now remove the list-item from the UL element
$this.fadeOut(function () {
$this.remove();
});
});
});

Categories