I am developing an Android app using Phonegap. In this app I have integrated Facebook login module. After user gets login into app, he/she can see friend list of his/her. I have done till this. What I have to do is, on clicking the particular friend name, that friend name should be added to another list. But my problem is this friend list is not clickable. How to make dynamic list clickable?Below is my code. What am I missing?
xyz.html
<div class="section">
<h1>Get user's friends</h1>
<div id="user-friends"></div>
<div class="button button-requires-connect" onclick="getUserFriends();hideButton(this);" />Get your friends</div>
<div class="info-requires-connect">You need to login before you can use this functionality.</div>
</div>
pqr.js
function getUserFriends() {
var markup = '<div class="data-header">Friends</div>';
for (var i=0; i < friendsInfo.length && i < 25; i++) {
var profilePictureUrl = '';
if (friendsInfo[i].picture.data) {
profilePictureUrl = friendsInfo[i].picture.data.url;
} else {
profilePictureUrl = friendsInfo[i].picture;
}
markup = markup + '<img src="' + profilePictureUrl + '">' + friendsInfo[i].name + '<br />';
}
document.getElementById('user-friends').innerHTML = markup;
}
You can wrap your 'friend-markup' for example in <li>s like that:
markup = markup + '<li><img src="' + profilePictureUrl + '">' + friendsInfo[i].name + '</li>'
and then use jQuery to bind clicks to the <li>s:
$('#user-friends').on('click', 'li', function() {
... do something
});
function getUserFriends() {
var markup = '<div class="data-header">Friends</div>';
for (var i=0; i < friendsInfo.length && i < 25; i++) {
var profilePictureUrl = '';
if (friendsInfo[i].picture.data) {
profilePictureUrl = friendsInfo[i].picture.data.url;
} else {
profilePictureUrl = friendsInfo[i].picture;
}
var clickableName = '<a href="" onclick="handleClick(this); return false;">'
+ friendsInfo[i].name + '</a>';
markup = markup + '<img src="' + profilePictureUrl + '">' + clickableName + '<br />';
}
document.getElementById('user-friends').innerHTML = markup;
}
Then write handler function
function handleClick(element/*which friend was clicked*/)
{
//do something with your friend
}
Or you can use jQuery to bind click event on element as #MiRaIT told
Related
I am writing a piece of code to basically call in the top money earner and the top five money earners in a given data set. While writing the code, I realized that there were a couple of spots where I was rewriting the code, basically copying and pasting it. While that works, I wanted to throw the duplicate portion of the code and call it from a function. However, that is not working and I don't exactly know why. Here is the code that is duplicated:
for (let i = 0; i < len; i++) {
html +=
'<li class="top">' +
'<h2>' +
topSalaries[i][8] +
'</h2>' +
'<h3>' +
topSalaries[i][11] +
'</h3>';
}
container.innerHTML = '<ul id = "topSalaries">' + html + '</ul>';
Here is the function I made to be called. However, when I call it, it's not working as expected, where the information shows up on the webpage. I'm using VS Code and am running this on live server so when I save, the webpage automatically updates.
function createHtmlElements(len, html) {
for (i = 0; i < len; i++) {
html +=
'<li class="top">' +
'<h2>' +
topFiveSalaries[i][8] +
'</h2>' +
'<h3>' +
topFiveSalaries[i][11] +
'</h3>' +
'</li>';
}
return html
}
function getTopSalaries(boston, container) {
const people = boston.data;
const len = 5; // only want top five
let topFiveSalaries = sortPeople(people).slice(0,len);
// create the list elements
html = createHtmlElements(len, html);
container.innerHTML = '<ul id = topSalaries">' + html + '</ul>';
}
For one thing topFiveSalaries is going to be undefined in the function createHtmlElements you've created, you must pass it to the function
Ok. So, Thanks Dave for the help. It looks like I also was missing a piece in that I needed to pass the array into the function as well. This is what I wrote and how I called it.
function getTopSalaries(boston, container) {
const people = boston.data;
const len = 5; // only want top five
var topFiveSalaries = sortPeople(people).slice(0,len);
let html = '';
// create the list elements
html = createHtmlElements(len, html, topFiveSalaries);
container.innerHTML = '<ul id = topSalaries">' + html + '</ul>';
}
function getTopEarner(boston, container){
const people = boston.data;
const len = 1;
let highEarner = sortPeople(people).slice(0,len);
var html = '';
// create the list elements
createHtmlElements(len, html, highEarner);
container.innerHTML = '<ul id = topSalaries">' + html + '</ul>';
}
// sort people by income in descending order
function sortPeople(people) {
people.sort(function(a, b) {
return b[11] - a[11];
})
return people
}
function createHtmlElements(len, html, array) {
for (i = 0; i < len; i++) {
html +=
'<li class="top">' +
'<h2>' +
array[i][8] +
'</h2>' +
'<h3>' +
array[i][11] +
'</h3>' +
'</li>';
}
return html
}
I'm working on a simple aplication based on localStorage and I have a problem with removing an item.
So, I'm adding new items to LS and display them as divs in for loop.
I created an easy "X" button on every card and here is a thing. How can I get an ID/position of this specific card after clicking "X" and pass it to remove function?
I'll present you my code:
// Display activities
var fetchActivities = function() {
var activities = JSON.parse(localStorage.getItem("activitie"));
const actCountContainer = document.getElementById("actCountContainer");
actCountContainer.innerHTML = "";
actCountContainer.innerHTML += "<div class='col-md-12'>" +
"<p>Your activities ("+activities.length+")";
var actCardContainer = document.getElementById("actCardContainer");
actCardContainer.innerHTML = "";
for (let i = 0; i < activities.length; i++) {
actCardContainer.innerHTML += '<div class="col-md-4">'+
'<div class="card">' +
'<div class="card-block">' +
'<div id="remove" class="remove">X</div>' +
'<h4 class="card-title">'+ activities[i].name + '</h4>' +
'<ul class="card-text">' +
'<li>Total time spent: 2h 25min 34sec</li>' +
'</ul>' +
'Go to this activity' +
'</div>' +
'</div>' +
'</div>'
}
const removeButton = document.getElementById("remove");
if (removeButton) {
removeButton.addEventListener("click", removeActivity);
};
};
// Add activity function
var addActivity = function() {
const actInput = document.getElementById("activityInput").value;
// Main activity object
var activity = {
name: actInput
};
if (localStorage.getItem("activitie") == null) {
var activities = [];
activities.push(activity);
localStorage.setItem("activitie", JSON.stringify(activities));
} else {
var activities = JSON.parse(localStorage.getItem("activitie"));
activities.push(activity);
localStorage.setItem("activitie", JSON.stringify(activities));
}
fetchActivities();
};
// Remove activity function
var removeActivity = function() {
};
const addButton = document.getElementById("addBtn");
addButton.addEventListener("click", addActivity);
I'd be very grateful if you can give me an idea how can I handle this remove function.
I would rewrite fetchActivities as follows
var fetchActivities = function() {
var activities = JSON.parse(localStorage.getItem("activitie"));
const actCountContainer = document.getElementById("actCountContainer");
actCountContainer.innerHTML = "";
actCountContainer.innerHTML += "<div class='col-md-12'>" +
"<p>Your activities ("+activities.length+")";
const actCardContainer = document.getElementById("actCardContainer");
actCardContainer.innerHTML = "";
let items = "";
for (let i = 0; i < activities.length; i++) {
itemsHTML += '<div class="col-md-4">'+
'<div class="card" data-id="' + activities[i].id + '">' +
'<div class="card-block">' +
'<div class="remove" data-id="' + activities[i].id + '">X</div>' +
'<h4 class="card-title">'+ activities[i].name + '</h4>' +
'<ul class="card-text">' +
'<li>Total time spent: 2h 25min 34sec</li>' +
'</ul>' +
'Go to this activity' +
'</div>' +
'</div>' +
'</div>'
}
actCardContainer.innerHTML = items;
// ... for attach event read on
};
Notes:
Do not set the same id if an element appears many times
Set innerHTML once not for each loop iteration
Set unique id for every item (you could generate random numbers for example)
To attach events you would need to do it as follows (taken from question ):
var removeLink = document.querySelectorAll('.remove');
Then you would loop:
for (var i = 0; i < deleteLink.length; i++) {
removeLink[i].addEventListener('click', function(event) {
var acrtivityId = event.currentTarget.getAttribute('data-id');
removeActivity(acrtivityId);
// Use
});
}
Now for the removal you can find current activity in the activity array and remove it. Use find and then splice for example. And save the change array to local storage. On creation assign an id.
In my javascript app, I insert a user message using the code:
var displayMessages = function(response, onBottom) {
var user = GLOBAL_DATA.user;
var acc = '';
for(var i=0; i<response.length; i+=1) {
var obj = response[i];
var acc_temp = "";
acc_temp += '<div class="message ' + (obj['user_id']==user['id'] ? 'message-right' : 'message-left') + '">';
acc_temp += '<div>' + Autolinker.link($(obj['message']).text()) + '</div>';
if (obj['user_id']!=user['id']) {
acc_temp += '<div class="message-details">' + obj['first_name'] + ' ' + obj['last_name'] + '</div>';
}
acc_temp += '<div class="message-details">' + obj['date_sent'] + '</div>';
acc_temp += '</div>';
acc = acc_temp + acc;
}
addMessage(acc, onBottom);
};
The problem is that, if obj['message'] = "<script>alert(1);</script>"; then what gets printed on the screen is "alert(1);" because I use .text(). How can I insert the string with the script tags, so that it looks exactly like that on the page? I don't want it to get executed.
Thanks
I use these helper functions.
function htmlEncode(value){
return $('<div/>').text(value).html();
}
function htmlDecode(value){
return $('<div/>').html(value).text();
}
I would escape the other variables as well if you are not sure that they will not have any executable code.
I solved it using this:
function escapeHTML(str) {
return $("<p></p>").text(str).html();
}
I think you'll need to wrap your object in a dummy tag, then you can retrieve the full html from that.
You'll have issues though, because you're using a script tag, which will be evaluated.
obj['message'] = "<script>alert(1);</script>";
>
$(obj['message']).text();
> "alert(1);"
$(obj['message']).html();
> "alert(1);"
$(obj['message']).wrapAll('<div>').text();
// alerts with 1
> "alert(1);"
Not using a script tag will work.
obj['message'] = "<span>alert(1);</span>";
>
$(obj['message']).wrapAll('<div>').text();
> "<span>alert(1);</span>"
I found working JavaScript code to feed into a jQuery mobile app here and made some minor modifications to it.
As I am very new to JavaScript - currently in the beginning of the JavaScript course at codeacademy.com - I didn't realize that document.write is not the best way to output/build your HTML.
When the .js file is loaded it's like a blank page which I believe is due to document.write. When I refresh, the RSS feed displays correctly. I'm having a very hard time figuring out what code I need to use to replace the document.write portions.
I've seen document.getElementById('div1').innerHTML='test' but not quite sure how to use this to replace document.write in the code below.
If someone would provide the alternative code for document.write in one of the sections below before the JSON code I would be extremely grateful.
/* configuration */
var maxLength = 10;
/* writing HTML */
document.write(
/* page begins */
'<div id="news" data-role="page" data-theme="a" data-title="NEWS">' +
' <div data-role="content">' +
' <ul data-role="listview" data-filter="true" id="dynamiclist" data-inset="true">'
);
for (var i=1; i<=maxLength; i++){
document.write(
'<li id="list' + i + '"> </li>'
);
}
document.write(
' </ul>' +
' </div>' +
'</div>'
);
for (i=1; i<=maxLength; i++){
document.write(
'<div data-role="page" id="article' + i + '">' +
' <div data-role="content">' +
' <h3 id="articleHeader' + i + '"> </h3>' +
' <div id="articleContent' + i + '" class="articleContent">' +
' <p id="articleDate' + i + '" class="articleDate"></p></div>' +
' <div data-role="controlgroup" data-type="horizontal">' +
' News' +
' <a href="#article' + String(i-1) + '" data-role="button" data-icon="arrow-l"' +
' data-inline="true" class="prevButton">Prev</a>' +
' <a href="#article' + String(i+1) + '" data-role="button" data-icon="arrow-r"' +
' data-inline="true" class="nextButton" data-iconpos="right">Next</a>' +
' </div>' +
' </div>' +
'</div>'
);
}
/* JSONP */
$(function(){
getOnlineFeed('http://rss.cnn.com/rss/money_news_economy.rss');
});
/* functions */
var getOnlineFeed = function(url) {
var script = document.createElement('script');
script.setAttribute('src', 'http://ajax.googleapis.com/ajax/services/feed/load?callback=listEntries&hl=ja&output=json-in-script&q='
+ encodeURIComponent(url)
+ '&v=1.0&num=' + maxLength);
script.setAttribute('type', 'text/javascript');
document.documentElement.firstChild.appendChild(script);
};
var getOfflineFeed = function(url) {
var script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', 'text/javascript');
document.documentElement.firstChild.appendChild(script);
};
var listEntries = function(json) {
if (!json.responseData.feed.entries) return false;
$('#widgetTitle').text(json.responseData.feed.title);
var articleLength =json.responseData.feed.entries.length;
articleLength = (articleLength > maxLength) ? maxLength : articleLength;
for (var i = 1; i <= articleLength ; i++) {
var entry = json.responseData.feed.entries[i-1];
$('#link' + i).text(entry.title);
$('#articleDate' + i).text(entry.pubdate);
$('#articleHeader' + i).text(entry.title);
$('#openButton' + i).attr('href', entry.link);
$('#articleContent' + i).append(entry.content);
}
$('#article1 .prevButton').remove();
$('#article' + articleLength + ' .nextButton').remove();
if (articleLength < maxLength) {
for (i = articleLength + 1; i <= maxLength; i++) {
$('#list' + i).remove();
$('#article' + i).remove();
}
}
};
$('#PageRefresh').click(function() {
location.reload();
});
Edit: I did not read the question carefully enough the first time - I now see that you're already using jQuery, but not before you do your DOM manipulation. So the rest of the answer stands - use .html() and manipulate the DOM only after the document is ready.
Here's my suggestion. Full disclaimer: this is not the only way to do it nor is it necessarily the "best" way to do it. I daresay it would be the most common way to do it, however.
Use jQuery - it will make your life so much easier. http://jquery.com
Make sure you do your DOM (Document Object Model) manipulation (inserting HTML elements to your document is a type of DOM manipulation, although I'm not sure that document.write() is) after it is ready. jQuery provides a nice method to do just this. http://api.jquery.com/ready/
$(function() {
// Handler for .ready() called
});
Use the .html() function within jQuery to write to your <body> element or a <div> element that you write directly in your HTML file. http://api.jquery.com/html/
HTML
<html>
<body>
<div id="content"></div>
</body>
</html>
jQuery
var page = "This should have the HTML you want to insert";
$(function() {
$("#content").html(page);
});
I've created my paginator directive:
myApp.directive("paginator", function($timeout) {
return {
restrict: "E",
link: function (scope, element, attr) {
var totalProducts = scope.productsConfig.total,
MAX_PER_PAGE = +(scope.productsConfig.limit),
pagesQty = Math.ceil(totalProducts/MAX_PER_PAGE),
markup = "";
//Add initial markup ul open tag
markup += "<ul class='ch-pagination'>";
//Add the previous button if needed
if(scope.lastStatus.p > 1) {
//Then add the previous button
var previousPage = +(scope.lastStatus.p) - 1;
markup += "<li><a ng-click='goToPage(" + previousPage + ")'>Previous</a></li>";
}
//Add the elements
for (var i = 1; i <= pagesQty; i++) {
if(scope.lastStatus.p == i){
var activeClass = "class='ch-pagination-current'";
} else {
activeClass = "";
}
markup += "<li " + activeClass + "><a ng-click='goToPage(" + i + ")'>" + i + "</a></li>"
}
//Add the next element if any
if(scope.lastStatus.p < pagesQty) {
//Then add the previous button
var nextPage = +(scope.lastStatus.p) + 1;
markup += "<li><a ng-click='goToPage(" + nextPage + ")'>Next</a></li>";
}
//Close the paginator
markup += "</ul>";
//Inject the code into the wrapper
$(".inventory-paginator").html(markup);
}
}
});
Line where my method is injected (among others):
markup += "<li " + activeClass + "><a ng-click='goToPage(" + i + ")'>" + i + "</a></li>"
Then my method goToPage is called when clicking the generated markup. When using the paginator, trying to click some page button, nothing happens, ng-click never executes goToPage method, even when the generated markup is:
"ng-click='goToPage(2)'"
The method inside the main controller:
$scope.goToPage = function (intPage) {
var requestUrl = $scope.buildSearchRequestUrl(intPage);
console.log("goToPage requestUrl: " + requestUrl);
//Request the data, on success show the table again
$http.get(requestUrl)
.success(function (data) {
$scope.inventoryData = data;
}).error(function (data) {
if(window.console){
console.log("The article couldnt be paused");
}
});
}
I´m guessing that Im missing some linking, but can´t figure out where, or why.
Thanks so much in advance,
Guillermo
Have you tried to compile it? A directive should be compiled whenever you want to invoke it from HTML.
See http://docs.angularjs.org/guide/directive
As roland pointed out you must use the $compile service to compile the html and then append it directive element. Without the $compile step angular has no way to link the ng-click directives.
myApp.directive("paginator", function($timeout, $compile) {
return {
restrict: "E",
link: function (scope, element, attr) {
var totalProducts = scope.productsConfig.total,
MAX_PER_PAGE = +(scope.productsConfig.limit),
pagesQty = Math.ceil(totalProducts/MAX_PER_PAGE),
markup = "";
//Add initial markup ul open tag
markup += "<ul class='ch-pagination'>";
//Add the previous button if needed
if(scope.lastStatus.p > 1) {
//Then add the previous button
var previousPage = +(scope.lastStatus.p) - 1;
markup += "<li><a ng-click='goToPage(" + previousPage + ")'>Previous</a></li>";
}
//Add the elements
for (var i = 1; i <= pagesQty; i++) {
if(scope.lastStatus.p == i){
var activeClass = "class='ch-pagination-current'";
} else {
activeClass = "";
}
markup += "<li " + activeClass + "><a ng-click='goToPage(" + i + ")'>" + i + "</a> </li>"
}
//Add the next element if any
if(scope.lastStatus.p < pagesQty) {
//Then add the previous button
var nextPage = +(scope.lastStatus.p) + 1;
markup += "<li><a ng-click='goToPage(" + nextPage + ")'>Next</a></li>";
}
//Close the paginator
markup += "</ul>";
//append and compile code to element
element.append($compile(markup)(scope));
});