student here! So I created a function that runs an $.ajax request, and for each item, I output html to the DOM. Well, check it out:
function getLinks (){
let blogLinks;
$.ajax(settings).then(function(data, status, xhr){
data.forEach(function(item, i, arr){
let id = item._id;
if (item.title){
blogLinks = `<li><a class="link" href=#${item.title}>${item.title}</a></li>`
$($findUl).append(blogLinks);
}
})
})
}
this var might be useful info as well:
let $blogViewAll =
$(`<div class="blog-view">
<h3>Check Out These Blogs</h3>
<div class="column-1">
<ul class="link-list"></ul>
</div>
<div class="column-2"></div>
</div>`);
let $findUl = $($blogViewAll).find('.link-list');
It's doing exactly what I want it to do and appending the links to the page. Now I want to click on the link and have the message body display on the page. So I'm trying this:
$findUl.on('click', function(e){
$.ajax(settings).then(function(data, status, xhr){
data.forEach(function(item, i, arr){
//since you clicked on that link
//I should look at what you clicked
//match it to the corresponding obj{item.body}
//and display it, but I don't know how to match and grab :(
})
})
});
And that's where I'm stuck! Any suggestions? Thanks!!!
(i'm using the tiny-za server btw)
You can access the current link the user clicked on in your initial argument e, via the property e.currentTarget:
$findUl.on('click', function(e){
// this gives you the DOM node that was clicked:
console.log(e.currentTarget);
}
A little shortcut to get the title using jQuery:
$findUl.on('click', function(e){
var title = $(e.currentTarget).text();
}
Another note, you should really only be loading the data from the server in that ajax call once, not every time something is clicked (which is sloooow).
I'd recommend loading the data into an object when the page loads, then using the obejct properties to append to the DOM. You'll be able to access that same object in future click handlers
Related
I am trying to obtain the value of a newly appended button, however it always logs undefined or nothing. I have tried many methods, such as .val(), .textContent, and .value, as I have found those on here.
Here is my code.
}).done(function (response) {
var lat = response.data[0].latitude;
var long = response.data[0].longitude;
//Appends new button based on recent search
searchHistory.append(`<button class="col-12 btn border-info m-1" id="prevSearch">${textInput.val().toLowerCase().split(' ').map((s) => s.charAt(0).toUpperCase() + s.substring(1)).join(' ')}</button>`);
var previousSearch = $("#prevSearch");
previousSearch.on('click', () => {
console.log($(this).val();
console.log(document.getElementById("prevSearch").textContent);
})
})
The first log under the click function returns undefined, while the second one returns the actual content. However it only works with the first button that is appended when I try a console.log("test").
So in summary, I have 2 Issues, I can't get the value of the button and only the first button works when tested with a simple console log.
Any help would be greatly appreciated.
You're getting the button by ID. If you're adding multiple elements with the same ID, that won't work since IDs must be unique within the document.
As for your button value, I'd say just use $(this).text(). Browser support for the element content as HTMLButtonElement.value has a patchy history.
I'd just use the following
const button = $("<button>", {
type: "button",
"class": "prevSearch col-12 btn border-info m-1",
text: textInput.val().replace(/(^| )[a-z]/g, c => c.toUpperCase())
}).appendTo(searchHistory)
button.on("click", function() {
console.log($(this).text())
})
You could also move the event handling to a delegated handler outside of this code that can handle all current and future button clicks
searchHistory.on("click", ".prevSearch", function() {
console.log($(this).text())
})
EDIT: This is a more sound approach, since provided answer may have bugs when implementing a tags, or img tags.
================================================================
I am calling blog data from an API. (I've reformatted the data into an array by month).
So far, the blog titles print to the web page. I'd like a user to be able to click a title and have its description revealed.
Here is some of my code so far:
var blogPosts = $('#blog-posts');
$.each(byMonth, function(key, value) {
var outer = byMonth[key]
$.each(outer, function(k, v) {
var inner = outer[k]
var monthBlogPosts = $('<div class = "month"> </div>').appendTo(blogPosts);
$.each(inner, function(i, obj) {
title = inner[i].Title
description = inner[i].Description
date = inner[i].DatePublished
$('<div class = "title-list"><h3 class = "unique-title">' + title + '</h3></div>').appendTo(monthBlogPosts)
// if a title is clicked, show its Description
showDescription(description);
})
})
});
function showDescription(d){
$('.unique-title').on('click', function(){
$('<p>' + d + '</p>').appendTo('body')
console.log(d)
})
}
When I click a title, all descriptions print instead of the matching description. I understand this is because I called the function in a nested loop, but I've also had trouble calling the description variable outside of it.
I have also tried
showDescription(title, description)
//...
function showDescription(t, d){
$(title).on('click', function(){
$('<p>' + d + '</p>').appendTo('body')
console.log(d)
})
}
but then nothing is printed to the html page.
Essentially, I'd like to grab the title index, and print it's respective description when its clicked.
you should use event delegation to attach a click event to the document that will bubble up and trigger when .title-list is the event target.
$(document).on('click', '.title-list', function(event) {
showDescription(event.currentTarget) // pass the element being clicked (we will need it later)
})
you would also need to modify the way you get the description.
you could store you description in a data attribute of .title-list like so:
$('<div class = "title-list" data-description="'+ description +'"><h3 class = "unique-title">' + title + '</h3></div>').appendTo(monthBlogPosts)
so you can now modify showDescription() so it would get the data from the element we pass to the function
function showDescription(element){
var d = $(element).data('description')
$('<p>' + d + '</p>').appendTo('body')
console.log(d)
})
So ok. From whatever I could understand (by looking at your code). You cannot register an event with simple on for dynamically added element. You have to use on delegate.
Try this
1) remove the function call (inside a loop)
2) delete the entire function showDescription and add event as below:
$('#blog-posts').on('click', '.unique-title',function(){
alert('title clicked').
});
3) As to display the description I think the best way will be to add the description in a div and hide it. Display it later once the title is clicked.
(inside the loop)
$('<div class = "desc" style="display:none">' + description + '</div>').appendTo(monthBlogPosts);
then on #2 above. Replace with this.
$('#blog-posts').on('click', '.unique-title',function(){
$(this).next('.desc').show(); //I am assuming desc will be next to the clicked title here. You can modify it as needed.
});
Finally, this is just an overview of a code so might not work as expected but I am pretty sure this should give you an idea and get you started
So I have a list of items with anchor a that successfully listen to the following event:
$('body[data-link="media"] #media_content a').on('click',function(e){
e.preventDefault();
var page = $('.page.active a')[0].innerHTML;
var date = $('.year_sorting .filter_years').val();
var id = $(e.currentTarget).data('media');
window.location.href = 'http://'+basePath+'media/content/'+id+'?date='+date+'&page='+page;
})
However in the same page, there is a filter allowing the user to change the year filter and once changed, the following execute and append a list of items that has the exact same layout as the a above $('body[data-link="media"] #media_content a'), which supposes to listen to the above event as well. the filter event is below:
$('.activity.filter_years').on('change',function(){
$('.pagination_ul').remove();
r_year = $(this).val();
$.get("media/getActivity",{type:'0',key:r_year}).done(function(d){
if(d.length>0){
$('#media_content').html('');
var ul = '<ul class="ap pagination-sm pagination_ul"></ul>';
$('.pagination_menu').append(ul);
for(var i=0;i<d.length;i++){
var p = ['',''];
if(!d[i].event_period){
p = ['style="color:#8A8A8A;"','style="color:#C7C7C7;"'];
}
if(locale=='en'){
var event = $('<div class="div_media_content_f2 '+d[i].pagination+' pagination-tr"> <div class="div_media_content_f2_3"> <span class="font12_bold">'+d[i].event_date+'</span> <div>'+d[i].event_title+'</div></div></div>')
}else if(locale=='hk'){
var event = $('<div class="div_media_content_f2 '+d[i].pagination+' pagination-tr"> <div class="div_media_content_f2_3"> <span class="font12_bold">'+d[i].event_date+'</span> <div>'+d[i].event_title_zh+'</div></div></div>')
}else {
var event = $('<div class="div_media_content_f2 '+d[i].pagination+' pagination-tr"> <div class="div_media_content_f2_3"> <span class="font12_bold">'+d[i].event_date+'</span> <div>'+d[i].event_title_cn+'</div></div></div>')
}
$('#media_content').append(event);
}
pagination('.pagination_ul','.pagination-tr',Math.ceil(d.length/20),false);
}else{
$('#div_news_content_right').html('').append('<div class="not_available">No content available</div>');
}
})
})
in which you can see the list of items are being appended into the layout by JS. However, even with the same layout $('body[data-link="media"] #media_content a'), such appended list of items do not listen to the onclick event. the above js codes are together in a separate js file apart from the html file where I tried to put the first a event into the html file but the new appended list of items still do not listen.
Cannot think of other work around at the moment, please help to see what would be the cause of it. Thank you.
Maybe simple try this.
$(document).on('click', 'body[data-link="media"] #media_content a')
If your element is dynamic create you should bind the click event on document and target what's element should dispatch the event.This is different to bind click only on element because the event will unbind while you remove the element.
Updated:
I'm not sure I've understand all the script you have but I try to simplify the issue.
This is the jsbin and its work correctly.
JSBin
I'm using a node.js module called horseman to scrape some data from a site which contains JavaScript. I'm having trouble figuring out how to click on each span element IF it contains a certain element within it, table in this case. This will expand that element and produce data available to scrape, which right now is hidden.
What I have right now
horseman
.open(url)
.click("span.title")
.waitforSelector("span.title")
.then(scrape)
The scrape function:
function scrape() {
return new Promise(function (resolve, reject) {
return getLinks()
.then(function (newLinks) {
links = links.concat(newLinks);
if (links.length < 1)
return horseman
.then(scrape);
}
})
.then(resolve);
});
}
And the getlinks function()
var links = [];
function getLinks() {
return horseman.evaluate(function () {
var links = [];
$("span.title").each(function (item) {
var link = {
title: $(this).text()
};
links.push(link);
});
return links;
});
}
My initial thoughts were that in the getLinks() function I could check if item contains table then click and then scrape, but not sure how to implement it.
The idea is to expand all the span elements, that are not already expanded, which means the data is visible and able to be scraped. I've hit a brick wall on what to do, so any help would be great!
The following code :
horseman
.open(url)
.click("span.title")
.waitforSelector("span.title")
.then(scrape)
...will not work because .click() horseman action only address single elements. Instead, you can try the following code that will work on many elements :
horseman
.open(url)
.evaluate(clickItems)
.waitforSelector("span.title XXX")
.then(scrape)
Where :
XXX should be the selector of the content inside the span.title (so the waitForSelector will actually wait). For example, let's consider this markup :
<span class="title"><!-- this is the clickable item -->
<table>...</table>
<div class="show-on-click">Blah blah</div>
</span>
In the above example, you would use .waitForSelector('span.item .show-on-click'). You have to find which selector does not exist until the data appears. (or use .wait(1000) instead)
clickItem function is defined as following (I see that you use jQuery so I will as well)
function clickItems() {
var $items = $('span.title:has(table)');
$items.each(function(index, $item) {
$item.click();
});
}
Note: This will click on all the elements span.title. You can modify the click element to add a table presence test in each $item, but I guess you can omit that if the other clicks do not do anything.
I know if I wanted to bind events to generated HTML, I'd need to use something like .on(), but I've only used it when binding events like .click().
I'm creating a web app that applys a list of colors. Colors are generated from a JSON file. Once fetched, I add it to the page, with certain information contained in attributes. I'd like to do something with the new generated HTML, which is list-elements. But what console.log() is showing me is there is nothing in the parent ul. Even though on the page I see the newly added content.
Here's the entire code based around it.
var setColors = function(){
getColors = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
//console.log(colors);
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
addColors();
}
addColors = function(){
var el = $('#picker').children;
$(el).each(function(){
console.log($(this));
});
}
return getColors();
}
$(function(){
setColors();
});
addColors() is where I'm having trouble with. The error says 'Uncaught TypeError: Cannot read property 'firstChild' of null. How can I work with the newly generated HTML?
You are missing parentheses on the children method:
var el = $('#picker').children();
Also, if you want the addColor method to be executed on the newly generated html, then you must add a call to it after the html is generated, from within the getJSON callback method.
addColors = function(){
var el = $('#picker').children;
$(el).each(function(){
console.log($(this));
});
}
A few issues:
missing end semi-color
missing parentheses on .children()
children() returns a jQuery object, no need for $(el)
Updated:
window.addColors = function(){
var $el = $('#picker').children();
$el.each(function(){
// do stuff here, but could attach each() to above, after children()
});
};