I need to create a form for comments with the ability to dynamically add them to the list. Each comment should have an assigned ID in consecutive order. The newest comment should be at the very bottom. Comments should be stored in the comments array. Each comment should have properties such as id (number) and text (string). Comments array must be empty when loaded initially. Each click on the "Add" button should create a new object inside the array and create element in the DOM tree.
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = commentForm.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.map(() => ({ id: 'nextId++', text: commentInput.value }));
}
createElement();
});
function createElement() {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = comments.text;
commentList.appendChild(newComment);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>
There are some issues in your code:
You are trying to access commentList from commentForm, but that element is outside of the commentForm. Use document object to access the element.
comments is an array from which you are trying to access text property, there is text property on comments.
You should pass the current input value to the function so that you can set the newly created LI's text with the value.
You should use push() instead of map() to push an item into the array. nextId is a variable but you are using that as if it is a string, you should remove the quotes around it.
For the better user experience, I will suggest you to clear the value of the input after creating the item.
Demo:
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = document.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.push({ id: nextId++, text: commentInput.value });
}
createElement(commentInput.value);
commentInput.value = '';
});
function createElement(ci) {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = ci;
commentList.appendChild(newComment);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button type="button" data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>
I'm making a game where after you click an image it will shuffle the array and repopulate the DOM. The idea is that you can't click the same image twice even though the images in the array are shuffled.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Document</title>
</head>
<body>
<!-- Tribute to Tom Whalen for the awesome Transformers pictures -->
<button id="shuffleButton" type="submit">Shuffle</button>
<div id="test"></div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="app.js">
</script>
</body>
</html>
The #testDiv is populated on page load and each time you click it shuffles the array and repopulates #testDiv.
Each image in the object has a name property related to the character's image.
I'm using Transformers.
Here's an example from the array of objects I am using.
var images = [
{
name: "bumblebee",
image: "assets/images/bumblebee.jpg"
},
{
name: "frenzy",
image: "assets/images/frenzy.jpg"
}];
JavaScript
/*Working for loop through images array */
function loop() {
for (var i =0; i < images.length; i++) {
var testImage = $('<img class="images">')
.attr('src', `${images[i].image}`)
.attr('data', `${images[i].name}`);
$(testDiv).append(testImage);
};
};
loop();
function Shuffle() {
images.sort(function(a, b) {
return 0.5 - Math.random()
});
loop();
};
$("#shuffleButton").on('click', function(){
$(testDiv).empty();
Shuffle();
console.log(images);
});
var count = 0;
$(document).on('click', '.images', function() {
$(testDiv).empty();
Shuffle();
console.log($(this).data());
});
I am assigning the name property to a data attribute and want to compare the data attributes with a click function.
When I console.log($(this).data()); I get a blank object with ___proto___ : Object and not the data attribute.
How would I grab the data attribute?
How woinI then store that into a variable?
How would I compare that data attribute inside of a variable to another element's data attribute?
You will need to send the data attributes name for your console.log function.
console.log($(this).data('name'));
I have attached an example below.
$(document).on('click', '#div', function() {
alert($(this).data('id'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div" data-id="test" >hello</div>
I'm playing around with an app that gets playlistItem data from the youtube api and lists all titles in the playlist on a page using knockout.js. I can get the list to load just fine, but I'm running into some issues while trying to add some extra functionality to the app.
Things I'm trying to add are a count of the total number of titles in the playlist and an input field that lets the user load another playlist. I currently can get neither to work.
Here's the app.js file:
// global variables
var ytResults=[];
var pageToken;
//var clipCount=ko.observable('');
var clipsDone=0;
var ytPlaylistID='PLpmQJ2D10iJx_GEYNZwAON38cluj0dNj4';
// function to create clip objects
var clip=function(data){
this.clipTitle=ko.observable(data.title);
};
function Model(){
var self=this;
//Create an observable array to store a list of clip objects
self.clipList=ko.observableArray([]);
self.clipCount=ko.observable('');
}
var model =new Model();
function ViewModel(){
var self = this;
self.errorMessage=ko.observable('');
self.loadPlaylist=ko.observable('');
errorHandling=function(){
self.errorMessage("Can't load the list and app");
};
fillList=function(){
for(i=0;i<ytResults.length;i++){
var clipData={
title: ytResults[i]
};
model.clipList.push(new clip(clipData));
};
};
var ytConnector=(function(){
var searchYtRequest=function(requestPayload, callback){
$.ajax({
url: requestPayload.url,
type: requestPayload.method,
}).done(function(data){
//console.log(data);
model.clipCount=data.pageInfo.totalResults;
ytResults.length=0;
for(i=0;i<data.items.length;i++){
ytResults=ytResults.concat(data.items[i].snippet.title);
};
fillList();
pageToken=data.nextPageToken;
clipsDone=clipsDone+50;
if(clipsDone<model.clipCount){
ytConnector.fetchDataFromYt();
};
}).fail(function(jqxhr, textStatus, error) {
// Let empty results set indicate problem with load.
// If there is no callback - there are no UI dependencies
self.errorMessage("Failed to load: " + textStatus + ", " + error);
}).always(function() {
typeof callback === 'function' && callback(ytResults);
});
};
// get playlist data from youtube
function fetchDataFromYt(){
if(pageToken!=null){
thisUrl='https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId='+ytPlaylistID+'&key=AIzaSyBFrCeUpPitoT6eOk_mq6Uza6etWtAH0oQ&pageToken='+pageToken;
}else{
thisUrl='https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId='+ytPlaylistID+'&key=AIzaSyBFrCeUpPitoT6eOk_mq6Uza6etWtAH0oQ';
};
var requestData = {
url: thisUrl,
method: 'GET',
};
searchYtRequest(requestData);
}return{
fetchDataFromYt: fetchDataFromYt,
};
})();
ytConnector.fetchDataFromYt();
thisPlaylist=function(){
ytPlaylistID=self.loadPlaylist();
console.log('playlist ID: '+ytPlaylistID);
};
}
ko.applyBindings(new ViewModel());
// PLpmQJ2D10iJzd1SPy7FlaaBFt07fhLSL3
Here's the html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Telex|Aldrich" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<title>Youtube playlist</title>
</head>
<body>
<div class="container">
<div class="errorMsg" data-bind="text: errorMessage, visible: errorMessage">
</div>
<div class="form-holder">
<form data-bind="submit: thisPlaylist">
playlist ID: <input type="text" size="50" data-bind="value: loadPlaylist, valueUpdate: 'afterkeydown'">
<button type="submit" data-bind="enable: loadPlaylist().length>0">submit</button>
</form>
</div>
<div class="info-holder">
clips in playlist:<span data-bind="text: model.clipCount"></span>
</div>
<ul class="listDiv" data-bind="foreach: model.clipList">
<li data-bind="text: clipTitle">
</ul>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="js/lib/knockout-3.2.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>
<script src="js/oauth-1.0a.js"></script>
<script src="js/app.js"></script>
</body>
model.clipCound is an observable object, the way you are setting the value is incorrect.
model.clipCount=data.pageInfo.totalResults;
You need to change to this
model.clipCount(data.pageInfo.totalResults);
I am programming with Backbone.js I am trying to run an API request when the user types in a query in the search box. However nothing happens when I type in a query.
Here is my JAVASCRIPT:
$(function(){
var SearchList = Backbone.Collection.extend({
url: "https://api.nutritionix.com/v1_1/search/taco?results=0%3A20&cal_min=0&cal_max=50000&fields=item_name%2Cbrand_name%2Citem_id%2Cbrand_id&appId=26952a04&appKey=78e2b31849de080049d26dc6cf4f338c",
initialize: function(){
this.bind("reset", function(model, options){
console.log("Inside event");
console.log(model);
});
},
//** 1. Function "parse" is a Backbone function to parse the response properly
parse:function(response){
//** return the array inside response, when returning the array
//** we left to Backone populate this collection
return response.hits;
}
});
// The main view of the application
var App = Backbone.View.extend({
initialize: function () {
this.model = new SearchList();
this.list = $('#listing');
},
el: 'document',
events: {
"keydown" : "prepCollection"
},
prepCollection: function(){
var name = $('input').val();
var newUrl = "https://api.nutritionix.com/v1_1/search/" + name + "?results=0%3A20&cal_min=0&cal_max=50000&fields=item_name%2Cbrand_name%2Citem_id%2Cbrand_id&appId=26952a04&appKey=78e2b31849de080049d26dc6cf4f338c";
this.model.set("url", newUrl);
this.model.fetch({
success: function (response, xhr) {
console.log("Inside success");
console.log(response.toJSON());
},
ERROR: function (errorResponse) {
console.log(errorResponse)
}
});
this.listenTo(this.model, 'sync', this.render);
},
render: function(){
var terms = this.model;
terms.each(function (term) {
this.list.append("<li>" + term.get('field')["brand_name"] + "</li>")
}, this);
}
});
var app = new App();
});
Here is my HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Interactive Food Guide</h1>
<div>
<input type="text" id="searchBox"> <br/><br/>
</div>
<ul id="listing"></ul>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Backbone and Underscore -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.1/backbone-min.js"></script>
<!-- apps functionality -->
<script src="js/app.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
url is a property of the model, not an attribute. Think of attributes as things you might persist to the server.
Change:
this.model.set("url", newUrl);
To:
this.model.url = newUrl;
Note that url can also be a function which returns a string, and that there is a default function already there which works for some more typical REST cases: http://backbonejs.org/#Model-url
Also JS variables and object keys are case-sensitive, so your key should be error nor ERROR.
After running the project I noticed a couple of other things wrong:
el: 'document' - This is the whole document including the head, Backbone works with the body or things within it. Fix this by changing it to el: 'body'
You were trying to access the field attribute - it is actually called fields and you can access it like so term.get('fields').brand_name
Other bonus fixes: Clear the list before appending new results, _.throttle prepCollection so that if letters are typed fast then it will only do 2 searches (one at the beginning and one at the end of the input). Change to _.debounce to only do one search at the end of the input.
Fiddle: http://jsfiddle.net/ferahl/2nLezvmg/1/
I have the following example code (you can cut'n'paste it if you want):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojo/dojo.xd.js"
djConfig="parseOnLoad:true"></script>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/claro/claro.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/tundra/tundra.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/grid/resources/Grid.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/grid/resources/claroGrid.css" />
<script>
dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.Button");
function init() {
var lData = {
items: [
{"position":"1","band":"Black Dyke","conductor":"Nick Childs"},
{"position":"2","band":"Carlton Main","conductor":"Philip McCann"},
{"position":"3","band":"Grimethorpe","conductor": "Allan Withington"},
{"position":"4","band":"Brighouse and Rastrick","conductor": "David King"},
{"position":"5","band":"Rothwell Temperance","conductor":"David Roberts"},
],
identifier: "position"
};
var dataStore = new dojo.data.ItemFileReadStore({data:lData});
var grid = dijit.byId("theGrid");
grid.setStore(dataStore);
dojo.connect(dijit.byId("theGrid"), 'onStyleRow', this, function (row) {
var theGrid = dijit.byId("theGrid")
var item = theGrid.getItem(row.index);
if(item){
var type = dataStore.getValue(item, "band", null);
if(type == "Rothwell Temperance"){
row.customStyles += "color:red;";
}
}
theGrid.focus.styleRow(row);
theGrid.edit.styleRow(row);
});
}
var plugins = {
filter: {
itemsName: 'songs',
closeFilterbarButton: true,
ruleCount: 8
}
};
dojo.ready(init);
function filterGrid() {
dijit.byId("theGrid").filter({band: dijit.byId('filterText').get('value')+'*'});
console.log(dijit.byId('filterText').get('value')+'*');
}
function resetFilter() {
dijit.byId("theGrid").filter({band: '*'});
dijit.byId('filterText').set('value','');
}
</script>
</head>
<body class="claro">
<input dojoType="dijit.form.TextBox" id="filterText" type="text" onkeyup="filterGrid()">
<button dojoType="dijit.form.Button" onclick="resetFilter()">Clear</button><br><br>
<table dojoType="dojox.grid.DataGrid" id="theGrid" autoHeight="auto" autoWidth="auto" plugins="plugins">
<thead>
<tr>
<th field="position" width="200px">Position</th>
<th field="band" width="200px">Band</th>
<th field="conductor" width="200px">Conductor</th>
</tr>
</thead>
</table>
</body>
</html>
The onStyleRow only gets fired when I click a row. Is there any way to have a button format rows based on their content? Either by having classes assigned to rows at creation time and then change the class values or loop through the rows and directly changing the style element of each based on it's contents.
EDIT
I have also tried creating the grid programmatically. While when created this way it does fire the onStyleRow at creation time it does not provide the same level of hightlighting the other method does.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojo/dojo.xd.js"
djConfig="parseOnLoad:true"></script>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/claro/claro.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/tundra/tundra.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/grid/resources/Grid.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/grid/resources/claroGrid.css" />
<style>
#grid {
height: 20em;
}
</style>
<script>dojoConfig = {async: true, parseOnLoad: false}</script>
<script>
dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.Button");
function init() {
var lData = {
items: [
{"position":"1","music":"Opera","band":"Black Dyke","conductor":"Nick Childs"},
{"position":"2","music":"Opera","band":"Carlton Main","conductor":"Philip McCann"},
{"position":"3","music":"Classical","band":"Grimethorpe","conductor": "Allan Withington"},
{"position":"4","music":"Classical","band":"Brighouse and Rastrick","conductor": "David King"},
{"position":"5","music":"Opera","band":"Rothwell Temperance","conductor":"David Roberts"},
],
identifier: "position"
};
var dataStore = new dojo.data.ItemFileReadStore({data:lData});
var layout = [[
{'name':'Position','field':'position','width':'50px'},
{'name':'Music Type','field':'music','width':'150px'},
{'name':'Band','field':'band','width':'200px'},
{'name':'Conductor','field':'conductor','width':'200px'}
]];
var grid = new dojox.grid.DataGrid({
id: 'grid',
store: dataStore,
structure: layout,
rowSelector: false,
selectionMode: 'extended',
onStyleRow: function(row) {
var item = this.getItem(row.index);
if(item){
var type = this.store.getValue(item, "band", null);
if(type == "Rothwell Temperance"){
row.customStyles += "color:red;";
}
}
}
});
grid.placeAt("gridDiv");
grid.startup();
}
var plugins = {
filter: {
itemsName: 'songs',
closeFilterbarButton: true,
ruleCount: 8
}
};
dojo.ready(init);
function filterGrid() {
dijit.byId("grid").filter({band: dijit.byId('filterText').get('value')+'*'});
console.log(dijit.byId('filterText').get('value')+'*');
}
function resetFilter() {
dijit.byId("grid").filter({band: '*'});
dijit.byId('filterText').set('value','');
}
</script>
</head>
<body class="claro">
<input dojoType="dijit.form.TextBox" id="filterText" type="text" onkeyup="filterGrid()">
<button dojoType="dijit.form.Button" onclick="resetFilter()">Clear</button><br><br>
<div id="gridDiv"></div>
</body>
</html>
Method 1 (doesn't format when grid is created, nice highlighting)
Method 2 (formats when grid created, no row highlighting)
A simple re-ordering of the init() function makes sure the onStyleRow function is bound before the data is added to the grid works.
function init() {
var lData = {
items: [
{"position":"1","band":"Black Dyke","conductor":"Nick Childs"},
{"position":"2","band":"Carlton Main","conductor":"Philip McCann"},
{"position":"3","band":"Grimethorpe","conductor": "Allan Withington"},
{"position":"4","band":"Brighouse and Rastrick","conductor": "David King"},
{"position":"5","band":"Rothwell Temperance","conductor":"David Roberts"},
],
identifier: "position"
};
var dataStore = new dojo.data.ItemFileReadStore({data:lData});
var grid = dijit.byId("theGrid");
dojo.connect(grid, 'onStyleRow', this, function (row) {
var item = grid.getItem(row.index);
if(item){
var type = dataStore.getValue(item, "band", null);
if(type == "Rothwell Temperance"){
row.customStyles += "color:red;";
//row.customClasses += " dismissed";
}
}
//theGrid.focus.styleRow(row);
//theGrid.edit.styleRow(row);
});
grid.setStore(dataStore);
}
Have you tried any of the following:
Style Dojox Grid Row depending on data
dojox DataGrid onStyleRow works first time, then not again
dojox.grid.DataGrid - onStyleRow needs update? (dojo 1.2.0)
You can also use Firebug to see if rows are getting assigned an ID and then change background of each row using onStyleRow.