I am building a simple script to understand how mvc works. I'm new to this and like to see if anyone out there can help me with this. I can't get my render function to display the content from the model. I get an error in my console:
"app.js:33 Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'."
Any help with this would be appreciated.
var model = {
farm: {
cow: "Moo!",
pig: "Oink!",
duck: "Quack!"
}
};
var controller = {
init: function() {
farmView.init();
},
getBarn: function() {
return model.farm;
}
};
var farmView = {
init: function() {
this.barn = document.getElementById('farm');
this.render();
},
render: function() {
var animals = controller.getBarn();
var examplediv = document.getElementById('cow');
this.barn.innerHTML = '';
var htmlStr = '';
htmlStr += '<span>' + model.farm.cow + '</span>' + '<span>' + model.farm.pig + '</span>';
this.barn.appendChild(htmlStr);
examplediv.appendChild(htmlStr);
}
};
controller.init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Farmcow</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="farm">
<div id="cow"></div>
</div>
<script src="app.js"></script>
</body>
</html>
As the error points out, appendChild() expects a Node, not a string. Use insertAdjacentHTML() with the argument beforeend:
var model = {
farm: {
cow: "Moo!",
pig: "Oink!",
duck: "Quack!"
}
};
var controller = {
init: function() {
farmView.init();
},
getBarn: function() {
return model.farm;
}
};
var farmView = {
init: function() {
this.barn = document.getElementById('farm');
this.render();
},
render: function() {
var animals = controller.getBarn();
this.barn.innerHTML = '';
var htmlStr = '';
htmlStr += '<span>' + model.farm.cow + '</span> <span>' + model.farm.pig + '</span>';
this.barn.insertAdjacentHTML('beforeend', htmlStr);
}
};
controller.init();
<div id="farm"></div>
The error message seems to say it clearly, what you pass as an argument to the appendChild is not of a Node type, it's a string.
You possibly meant to call document.createElement first to have a new element, then set its innerHTML and only then append it. This will work, as the newly created element will have a type accepted by the appendChild.
Related
I have these javascript files. It is really small test project to understand how MVC works with javascript. When I try to click 'Delete Bloak' button, nothing happens except with last button, which has id of 100. The other buttons do not have any click event attached to them. Why?
Controller.js
var BloakController = function BloakController(model, view) {
this.model = model;
this.view = view;
}
BloakController.prototype.init = function init() {
this.view.onAddBloak = this.addBloak.bind(this);
this.view.onDeleteBloak = this.deleteBloak.bind(this);
this.view.onGetAllBloaks = this.getAllBloaks.bind(this);
}
BloakController.prototype.addBloak = function addBloak() {
}
BloakController.prototype.getAllBloaks = function getAllBloaks() {
this.model.getAllBloaks(this.getAllBloaksList.bind(this));
}
BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
for (var i = 0; i < allBloaksList.length; i++) {
var bloak = allBloaksList[i];
var bloakDataObjectModel = {
title: bloak.title,
body: bloak.body,
userId: bloak.userId,
id: bloak.id
};
this.view.render(bloakDataObjectModel);
}
}
BloakController.prototype.deleteBloak = function deleteBloak(id) {
this.model.deleteBloak(id, this.getAllBloaksList.bind(this));
}
Model.js
var BloakModel = function BloakModel(XMLHttpRequest) {
this.XMLHttpRequest = XMLHttpRequest;
}
BloakController.prototype.addBloak = function addBloak() {
}
BloakModel.prototype.getAllBloaks = function getAllBloaks(callback) {
var request = new this.XMLHttpRequest;
request.onload = function onLoad(e) {
var ajaxResponse = JSON.parse(e.currentTarget.responseText);
callback(ajaxResponse);
}
request.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
request.send();
}
BloakModel.prototype.deleteBloak = function deleteBloak(id, callback) {
console.log(id);
var request = new this.XMLHttpRequest;
request.onload = function onLoad(e) {
var ajaxResponse = JSON.parse(e.currentTarget.responseText);
callback(ajaxResponse);
}
request.open('DELETE', 'https://jsonplaceholder.typicode.com/posts/' + id, true);
request.send();
}
View.js
var BloakView = function BloakView(domElement) {
this.domElement = domElement;
this.onAddBloak = null;
this.onDeleteBloak = null;
this.onGetAllBloaks = null;
}
BloakView.prototype.render = function render(bloakDataObjectModel) {
this.domElement.innerHTML += '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';
var addBloakButton = document.getElementById('addBloakButton');
var deleteBloakButton = document.getElementById(bloakDataObjectModel.id);
addBloakButton.addEventListener('click', this.onAddBloak);
deleteBloakButton.addEventListener('click', this.onDeleteBloak);
}
App.js
var bloakModel = new BloakModel(XMLHttpRequest);
var targetElement = document.getElementById('bloaksContainer');
var bloakView = new BloakView(targetElement);
var bloakController = new BloakController(bloakModel, bloakView);
bloakController.init();
bloakController.getAllBloaks();
Update
As it turns out I was totally wrong, having done more research, looks like using the innerHTML property will cause for the destruction of all child elements, read more about it here. Either way, the solutions provided will still work, turns out I didn't know that myself.
My Brain Fart
I should've realised that I was wrong initially, if I was right then you'd have an error stating that you can't attach an event listener to null or undefined, my bad, stupid mistake on my part!
Idea
Having very briefly looked over your code, I think it's safe to say that your render function is somewhat faulty, due to using innerHTML, it's not parsing the updated HTML fast enough for the event listeners to work...
Having tested it myself, and having just done the following:
setTimeout(() => {
var addBloakButton = document.getElementById('addBloakButton');
var deleteBloakButton = document.getElementById(bloakDataObjectModel.id);
addBloakButton.addEventListener('click', this.onAddBloak);
deleteBloakButton.addEventListener('click', this.onDeleteBloak);
}, 10);
It worked. So, if you don't want to change too much, you could do what I've done above, or alternatively, you could do something along the lines of this:
var div = document.createElement("div");
div.innerHTML = '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';
this.domElement.append(div);
var addBloakButton = document.getElementById('addBloakButton');
var deleteBloakButton = div.querySelector('input[id*="' + bloakDataObjectModel.id + '"]');
addBloakButton.addEventListener('click', this.onAddBloak);
deleteBloakButton.addEventListener('click', this.onDeleteBloak);
Furthermore, what I would do as a more solid and reliable approach is to implement an onRender function within the view, essentially stating that once and only once all of the relevant HTML has been rendered, then fire the onRender method. Or maybe something like view.dispatchEvents, etc... Specifically within this example, I'd run such a function within the controller, so once getAllBloaksList has rendered all of the relevant data, you can then fire a dispatchEvents function, like so:
// Within the controller part...
BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
for (var i = 0; i < allBloaksList.length; i++) {
var bloak = allBloaksList[i];
var bloakDataObjectModel = {
title: bloak.title,
body: bloak.body,
userId: bloak.userId,
id: bloak.id
};
this.view.render(bloakDataObjectModel);
}
this.view.dispatchEvents();
};
// .. Within the View part...
BloakView.prototype.dispatchEvents = function () {
var list = this.domElement.querySelectorAll(".bloakContainer");
var todo = this.onDeleteBloak;
list.forEach(function (div) {
var deleteButton = div.querySelector("input[type=button]");
deleteButton.onclick = function () {
todo(this.id);
};
});
};
Demo
// Controller.
var BloakController = function BloakController(model, view) {
this.model = model;
this.view = view;
};
BloakController.prototype.init = function init() {
this.view.onAddBloak = this.addBloak.bind(this);
this.view.onDeleteBloak = this.deleteBloak.bind(this);
this.view.onGetAllBloaks = this.getAllBloaks.bind(this);
};
BloakController.prototype.addBloak = function addBloak() {
// todo
};
BloakController.prototype.getAllBloaks = function getAllBloaks() {
this.model.getAllBloaks(this.getAllBloaksList.bind(this));
};
BloakController.prototype.getAllBloaksList = function getAllBloaksList(allBloaksList) {
for (var i = 0; i < allBloaksList.length; i++) {
var bloak = allBloaksList[i];
var bloakDataObjectModel = {
title: bloak.title,
body: bloak.body,
userId: bloak.userId,
id: bloak.id
};
this.view.render(bloakDataObjectModel);
}
this.view.dispatchEvents();
};
BloakController.prototype.deleteBloak = function deleteBloak(id) {
this.model.deleteBloak(id, this.getAllBloaksList.bind(this));
};
// Model.
var BloakModel = function BloakModel(XMLHttpRequest) {
this.XMLHttpRequest = XMLHttpRequest;
};
BloakController.prototype.addBloak = function addBloak() {
// todo
};
BloakModel.prototype.getAllBloaks = function getAllBloaks(callback) {
var request = new this.XMLHttpRequest;
request.onload = function onLoad(e) {
var ajaxResponse = JSON.parse(e.currentTarget.responseText);
callback(ajaxResponse);
}
request.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
request.send();
};
BloakModel.prototype.deleteBloak = function deleteBloak(id, callback) {
console.log(id);
var request = new this.XMLHttpRequest;
request.onload = function onLoad(e) {
var ajaxResponse = JSON.parse(e.currentTarget.responseText);
callback(ajaxResponse);
}
request.open('DELETE', 'https://jsonplaceholder.typicode.com/posts/' + id, true);
request.send();
};
// View.
var BloakView = function BloakView(domElement) {
this.domElement = domElement;
this.onAddBloak = null;
this.onDeleteBloak = null;
this.onGetAllBloaks = null;
};
BloakView.prototype.render = function render(bloakDataObjectModel) {
this.domElement.innerHTML += '<div class="bloakContainer"><h3>' + bloakDataObjectModel.title + '</h3><p class="bloakContent">' + bloakDataObjectModel.body + '</p><label class="bloakAuthor">' + bloakDataObjectModel.userId + '</label><br /><label class="bloakDate">' + bloakDataObjectModel.id + '</label><br /><input id=' + bloakDataObjectModel.id + ' type="button" value="Delete Bloak" /></div>';
}
BloakView.prototype.dispatchEvents = function() {
var list = this.domElement.querySelectorAll(".bloakContainer");
var todo = this.onDeleteBloak;
var addBloakButton = document.getElementById('addBloakButton');
addBloakButton.addEventListener('click', this.onAddBloak);
list.forEach(function(div) {
var deleteButton = div.querySelector("input[type=button]");
deleteButton.onclick = function() {
todo(this.id);
};
});
};
// App.
var bloakModel = new BloakModel(XMLHttpRequest);
var targetElement = document.getElementById('bloaksContainer');
var bloakView = new BloakView(targetElement);
var bloakController = new BloakController(bloakModel, bloakView);
bloakController.init();
bloakController.getAllBloaks();
<button id="addBloakButton">Add</button>
<div id="bloaksContainer">
<!-- DHTML -->
</div>
The code this.domElement.innerHTML += ...; is incorrect.You are using a += operator which adds something with itself and then assign the new value.
The new value is the result of existing innerHTML and new HTML. So the new value [HTML] will not have the previously added event handlers, it will just copy the HTML.
You can use appendChild method to append new elements.
Also, you should avoid using the for-loop with innerHTML because it will keep on updating DOM and you may see flickers on the screen. If you must use innerHTML, you should
Create the entire HTML [innerHTML] together and inject at once,
Then use event delegation to add event handlers.
Event delegation is a technique to bind events to a top-level container so that you do not need to worry about attaching the event listener to each item.
You can read more on DOM here.
Looking at this example https://editor.datatables.net/examples/inline-editing/simple
It allow you edit the text if you click on the cell. If you click on the cell - it will then render into input tag
My case, I want a bit different. Each row have a Edit button, user click on the Edit button then all input tags will be displayed on that row.
I am unable to find any demo or how to do this on datatables, can you advice?
when click td render into input code like this:
$td.click(function () {
var OriginalContent = $(this).text();
$(this).addClass("cellEditing");
$(this).html('<input type="text" value="'+ OriginalContent + '" style="width: 100%"/>');
$(this).children().first().focus();
$(this).children().first().keypress(function (e) {
if (e.which == 13) {
var newContent = $(this).val();
$(this).parent().text(newContent);
$(this).parent().removeClass("cellEditing");
}
});
$(this).children().first().blur(function(){
$(this).parent().text(OriginalContent);
$(this).parent().removeClass("cellEditing");
});
});
click on the Edit button then all input tags will be displayed on that row:
1.row data in memory
2.when click button,find this row's td config (which UI to render:input,select,radio....)
3.switch UI and row data like angularjs two-way Data Binding
Spend one hour for this Demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
</head>
<body>
<div id="newGrid"></div>
<script>
function Grid(){
this.config = {
id:"newGrid",
data:[
{name:"alert",age:"18"},
{name:"Jone",age:"28"}
]
};
this.rows = [];
}
Grid.prototype.render =function(){
var html = '<table class="table table-bordered">' +
'<tr>' +
'<th>Name</th>' +
'<th>age</th>' +
'<th></th>' +
'</tr>' +
'</table>';
var $table = $(html);
for(var i= 0,item;item=this.config.data[i++];){
var newRow = new Row();
this.rows.push(newRow);
var rowDom = newRow.render(item);
$table.append(rowDom);
}
$("#"+this.config.id).append($table);
};
function Row(){
this.cells = {};
}
Row.prototype.render = function(row){
var _this = this;
var nameCell= new Cell(row.name);
var ageCell = new Cell(row.age);
this.cells = {
name:nameCell,
age:ageCell
};
var $editBtn= $("<button>Edit</button>").click(function(){
_this.editRow();
});
var $saveBtn= $("<button>Save</button>").click(function(){
_this.saveRow();
});
return $("<tr></tr>")
.append($("<td></td>").append(nameCell.$Dom))
.append($("<td></td>").append(ageCell.$Dom))
.append($("<td></td>").append($editBtn).append($saveBtn));
};
Row.prototype.editRow = function(){
for(var key in this.cells){
this.cells[key].editorCell();
}
};
Row.prototype.saveRow = function(){
var data = {};
for(var key in this.cells){
//console.log(key+"="+this.cells[key].editor.getValue());
data[key] = this.cells[key].editor.getValue()
}
alert(JSON.stringify(data))
};
function Cell(value){
this.$Dom = $("<td></td>");
this.editor = null;
this.render(value);
}
Cell.prototype.render = function(value){
this.editor = new Forms["Span"](value);
return this.$Dom.append(this.editor.$Dom);
};
Cell.prototype.editorCell = function(){
this.editor = new Forms["Input"](this.editor.getValue());
this.$Dom.html(this.editor.$Dom)
};
var Forms = {};
//Span Object
Forms.Span = function(value){
this.$Dom = $('<span>'+ value +'</span>');
};
Forms.Span.prototype.setValue = function(value){
this.$Dom.text(value);
};
Forms.Span.prototype.getValue = function(){
return this.$Dom.text();
};
//Input Object
Forms.Input = function(value){
this.$Dom = $('<input type="text" style="width:100%">');
this.setValue(value);
};
Forms.Input.prototype.setValue = function(value){
this.$Dom.val(value);
};
Forms.Input.prototype.getValue = function(){
return this.$Dom.val();
};
//Init Grid
$(document).ready(function(){
new Grid().render();
})
</script>
</body>
</html>
I am kind of new to API's and I have the hang of some of it, but not quite all of it yet. When I type in a tag, such as "HTML" or "JavaScript", I am trying to write a program that will give me the top answerers for that tag. When I type in my result, it says "20 results for undefined," so for some reason, I don't think my code is "communicating" propertly.
I think it has to do with my "Tagged: answered" part, but I'm not sure. What do you guys think?
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stack Overflow AJAX Demo</title>
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
<div class="intro">
<div class="left stack-image"></div>
<div class="explanation">
<h1>StackOverflow Reputation Builder</h1>
<p>This app lets you search by topic for unanswered questions on Stack Overflow to help you build your reputation. Find unanswered questions for a topic you know about, write quality answers, and watch your reputation go up.</p>
<p>Sometimes, you also need some inspiration. This page also lets you search for the top answerers for a tag. If you want to rise to the top ranks for a topic, see how many reputation points you'll need to aim for!</p>
</div>
</div>
<hr>
<div class="stack">
<h3>Get Unanswered Questions</h3>
<p>Find unanswered questions by tag. For multiple tags, use a semi-colon to separate.</p>
<form class="unanswered-getter" onsubmit="return false;">
<input type="text" placeholder='e.g., "HTML" or "HTML;CSS"' name="tags" size="30" autofocus required>
<input type="submit" value="Submit">
</form>
<h3>View the Top Answerers for a Tag</h3>
<form class="inspiration-getter" onsubmit="return false;">
<input type="text" placeholder="e.g., jQuery" name="answerers" size="30" required>
<input type="submit" value="Submit">
</form>
</div>
<div class="results-container">
<div class="search-results"></div>
<div class="results"></div>
</div>
</div>
<div class="templates hidden">
<dl class="result question">
<dt>Question</dt>
<dd class="question-text"></dd>
<dt>Asked</dt>
<dd class="asked-date"></dd>
<dt>Viewed</dt>
<dd class="viewed"></dd>
<dt>Asker</dt>
<dd class="asker"></dd>
</dl>
<dl class="resultAnswerer">
<dt>Name of Answerer:</dt>
<dd class="answererName"></dd>
<dt>Score:</dt>
<dd class="scoreAmount"></dd>
<dt>Reputation:</dt>
<dd class="reputationAmount"></dd>
</dl>
<div class="error">
<p>Uh-oh! Something went wrong with your request. Here's what we know:</p>
</div>
</div>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Javascript:
var showQuestion = function(question) {
var result = $('.templates .question').clone();
var questionElem = result.find('.question-text a');
questionElem.attr('href', question.link);
questionElem.text(question.title);
var asked = result.find('.asked-date');
var date = new Date(1000*question.creation_date);
asked.text(date.toString());
var viewed = result.find('.viewed');
viewed.text(question.view_count);
var asker = result.find('.asker');
asker.html('<p>Name: <a target="_blank" '+
'href=http://stackoverflow.com/users/' + question.owner.user_id + ' >' +
question.owner.display_name +
'</a>' +
'</p>' +
'<p>Reputation: ' + question.owner.reputation + '</p>'
);
return result;
};
var topScorers = function(score){
var result = $('.templates .resultAnswerer').clone();
var answererDisplayName = result.find('answererName');
answererDisplayName.text(score.display_name);;
var answererScore = result.find('scoreAmount');
answererScore.text(score.score);
var answererReputation = result.find('reputationAmount');
answererReputation.text(score.reputation);
return result;
}
var showSearchResults = function(query, resultNum) {
var results = resultNum + ' results for <strong>' + query + '</strong>';
return results;
};
var showError = function(error){
var errorElem = $('.templates .error').clone();
var errorText = '<p>' + error + '</p>';
errorElem.append(errorText);
};
var getUnanswered = function(tags) {
var request = { tagged: tags,
site: 'stackoverflow',
order: 'desc',
sort: 'creation'};
$.ajax({
url: "http://api.stackexchange.com/2.2/questions/unanswered",
data: request,
dataType: "jsonp",//use jsonp to avoid cross origin issues
type: "GET",
})
.done(function(result){ //this waits for the ajax to return with a succesful promise object
var searchResults = showSearchResults(request.tagged, result.items.length);
$('.search-results').html(searchResults);
$.each(result.items, function(i, item) {
var question = showQuestion(item);
$('.results').append(question);
});
})
.fail(function(jqXHR, error){
var errorElem = showError(error);
$('.search-results').append(errorElem);
});
};
var getAnswerers = function(answered){
var request = {
tagged: answered,
site: 'stackoverflow',
order: 'desc',
sort: 'score',
};
$.ajax({
url: "http://api.stackexchange.com/2.2/tags/" + answered + "/top-answerers/all_time",
data: request,
dataType: "jsonp",
type: "GET",
})
.done(function(result){
var searchResults = showSearchResults(request.tagged, result.items.length);
$('.search-results').html(searchResults);
$.each(result.items, function(i,item){
var score = topScorers(item);
$('.results').append(score);
});
})
.fail(function(jqXHR, error){
var errorElem = showError(error);
$('.search-results').append(errorElem);
});
}
$(document).ready( function() {
$('.unanswered-getter').submit(function(){
$('.results').html('');
var tags = $(this).find("input[name='tags']").val();
getUnanswered(tags);
});
$('.inspiration-getter').submit(function(){
$('.results').html('');
var answered = $(this).find("input[name='tags']").val();
getAnswerers(answered);
});
});
You're probably calling the getAnswer on submit of a form. Form submission will destroy the Javascript context. To make it work correctly, you should use simple click instead of submit.
Check code below:
<html>
<head>
<script src="jquery-1.10.2.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<style>
img {
width: 50px;
height: 50px;
}
</style>
</head>
<body>
<input class="tag"/><button class="search">Search</button>
<div class="search-results"></div>
<script>
var topScorers = function(score){
//Clone result template ode
var result = $('.templates .resultAnswerer').clone();
//set the display name in result
var answererDisplayName = result.find('answererName');
answererDisplayName.text(score.display_name);;
//set the user score amount in result
var answererScore = result.find('scoreAmount');
answererScore.text(score.score);
//set the user reputation amount in result
var answererReputation = result.find('reputationAmount');
answererReputation.text(score.reputation);
return result;
}
var getAnswerers = function(answered){
//the parameters I need to pass in our request to StackOverflow's API
var request = {
tagged: answered,
site: 'stackoverflow',
order: 'desc',
sort: 'score',
};
$.ajax({
//the parameters I need to pass in our request to stackOverFlow's API
//this is the endpoint that I want to use
url: "http://api.stackexchange.com/2.2/tags/" + answered + "/top-answerers/all_time",
data: request,
dataType: "jsonp",
type: "GET",
})
//what the function should do if successful
.done(function(result){
//this gives you the search result description
//var searchResults = showSearchResults(request.tagged, result.items.length);
showResults(result.items);
})
//what the function should do if it fails
.fail(function(jqXHR, error){
$('.search-results').text(JSON.stringify(error));
});
}
function showResults (result) {
var tbody = "";
var thead = "";
var firstEl = result[0];
var propArr = [];
for(var prop in firstEl) {
if(typeof firstEl[prop] !== "object") {
thead += "<th>" + prop + "</th>";
propArr.push(prop);
} else {
var obj = firstEl[prop]
for(var propdeep in obj) {
thead += "<th>" + propdeep + "</th>";
propArr.push(propdeep);
}
}
}
result.forEach(function (rowEl) {
var row = "<tr>";
var currentEl = null;
for(var i = 0; i < propArr.length; i++) {
currentEl = rowEl[propArr[i]];
if(currentEl === undefined) {
currentEl = rowEl["user"][propArr[i]]
}
if(propArr[i] !== "profile_image") {
row += "<td>"+ currentEl + "</td>";
} else {
row += "<td><img src='"+ currentEl + "'></img></td>";
}
}
row += "</tr>";
tbody += row;
});
var table = "<table><thead>" + thead + "</thead><tbody>" + tbody + "</tbody></table>";
$('.search-results').html(table);
}
$('.search').click(function(){
//zero out results if previous search has run
$('.search-results').html('');
//separate line of code
var answered = $(".tag").val();
getAnswerers(answered);
});
</script>
</body>
</html>
*Edit***********************
Since you're using "return false;" in submit of the forms, it's not submitting. That's actually clever as it saves us from having to attach both mouse and key events
There were 3 bugs in the code you posted last.
1> Input text field name is "answerers" but in the submit() function it's used as "tags".
var answered = $(this).find("input[name='tags']").val();
// should be
var answered = $(this).find("input[name='answerers']").val();
2> Data returned from the inspiration-getter form call has different structure than used. Actual structure of each item is as below.
{
post_count: 0,
score: 0,
user: {
display_name: "",
link: "",
profile_image: "",
reputation: 0,
user_id: 0,
user_type: ""
}
}
3> There should be a . (dot) before the selectors used in the topScorers() function.
var answererDisplayName = result.find('answererName'); //should be '.answererName'
answererDisplayName.text(score.display_name);;
var answererScore = result.find('scoreAmount'); //should be '.scoreAmount'
answererScore.text(score.score);
var answererReputation = result.find('reputationAmount'); //should be '.reputationAmount'
That's it,
Here is the working code after corrections,
app.js
var showQuestion = function(question) {
var result = $('.templates .question').clone();
var questionElem = result.find('.question-text a');
questionElem.attr('href', question.link);
questionElem.text(question.title);
var asked = result.find('.asked-date');
var date = new Date(1000*question.creation_date);
asked.text(date.toString());
var viewed = result.find('.viewed');
viewed.text(question.view_count);
var asker = result.find('.asker');
asker.html('<p>Name: <a target="_blank" '+
'href=http://stackoverflow.com/users/' + question.owner.user_id + ' >' +
question.owner.display_name +
'</a>' +
'</p>' +
'<p>Reputation: ' + question.owner.reputation + '</p>'
);
return result;
};
var topScorers = function(item){
var user = item.user;
var result = $('.templates .resultAnswerer').clone();
var answererDisplayName = result.find('.answererName');
answererDisplayName.text(user.display_name);;
var answererScore = result.find('.scoreAmount');
answererScore.text(item.score);
var answererReputation = result.find('.reputationAmount');
answererReputation.text(user.reputation);
return result;
}
var showSearchResults = function(query, resultNum) {
var results = resultNum + ' results for <strong>' + query + '</strong>';
return results;
};
var showError = function(error){
var errorElem = $('.templates .error').clone();
var errorText = '<p>' + error + '</p>';
errorElem.append(errorText);
};
var getUnanswered = function(tags) {
var request = { tagged: tags,
site: 'stackoverflow',
order: 'desc',
sort: 'creation'};
$.ajax({
url: "http://api.stackexchange.com/2.2/questions/unanswered",
data: request,
dataType: "jsonp",//use jsonp to avoid cross origin issues
type: "GET",
})
.done(function(result){ //this waits for the ajax to return with a succesful promise object
var searchResults = showSearchResults(request.tagged, result.items.length);
$('.search-results').html(searchResults);
$.each(result.items, function(i, item) {
var question = showQuestion(item);
$('.results').append(question);
});
})
.fail(function(jqXHR, error){
var errorElem = showError(error);
$('.search-results').append(errorElem);
});
};
var getAnswerers = function(answered){
var request = {
tagged: answered,
site: 'stackoverflow',
order: 'desc',
sort: 'score',
};
$.ajax({
url: "http://api.stackexchange.com/2.2/tags/" + answered + "/top-answerers/all_time",
data: request,
dataType: "jsonp",
type: "GET",
})
.done(function(result){
var searchResults = showSearchResults(request.tagged, result.items.length);
$('.search-results').html(searchResults);
$.each(result.items, function(i,item){
var score = topScorers(item);
$('.results').append(score);
});
})
.fail(function(jqXHR, error){
var errorElem = showError(error);
$('.search-results').append(errorElem);
});
}
$(document).ready( function() {
$('.unanswered-getter').submit(function(){
$('.results').html('');
var tags = $(this).find("input[name='tags']").val();
if(!tags || $.trim(tags) === "") {
alert("input is empty");
} else {
getUnanswered(tags);
}
});
$('.inspiration-getter').submit(function(){
$('.results').html('');
var answered = $(this).find("input[name='answerers']").val();
if(!answered || $.trim(answered) === "") {
alert("input is empty");
} else {
getAnswerers(answered);
}
});
});
I figured out part of what I did wrong.
I had name 'tags" instead of name 'answerers', so part of my problem is fixed.
Now if I get results, it will say "20 results of HTML" or "20 results of CSS" or whatever tag I use.
Now I just need to figure out why all of my results are blank. For some reason, it works for one button, but not the other.
JavaScript:
$('.inspiration-getter').submit(function(){
$('.results').html('');
var answered = $(this).find("input[name='tags']").val();
getAnswerers(answered);
});
HTML:
<form class="inspiration-getter" onsubmit="return false;">
<input type="text" placeholder="e.g., jQuery" name="answerers" size="30" required>
<input type="submit" value="Submit">
</form>
How can I send two Javascript values to a xively feed, One as the ID and one as the value. Current code is. I want to get those values in to a xively feed so I can access them via an arduino with wifi. Unfortunately getting the values directly from the website from JS does not seem straight forward so this is my workaround unless anyone has a better way of accessing this data from an arduino. Using this example for reference execept i want to send the data from website rather than retrieve it. http://xively.github.io/xively-js/tutorial/
<!DOCTYPE html >
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script src="jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="parse-1.2.16.min.js"></script>
</head>
<body>
<script>
jQuery(function($){
xively.setKey( "APIKEY" );
Parse.initialize("APIKEY", "APIKEY");
var mValue = Parse.Object.extend("mValue");
var queryLngLat = new Parse.Query(mValue);
var classObject = new mValue();
var existingMarkers= [];
var databaseMarkers= [];
var counter = 0;
var div ;
var resultsLength;
setInterval(function update() {
queryLngLat.notEqualTo("longAndLat", null);
queryLngLat.find({
success: function(results) {
console.log("Successfully retrieved " + results.length + " scores.");
for (var j = 0; j < results.length; j++ ) {
databaseMarkers = (results[j].attributes.longAndLat);
resultsLength = results.length;
counter++;
var markerValueRead = results[j].attributes.Val;
CoordsPush = databaseMarkers.substring(1, databaseMarkers.length - 1);
div = document.createElement("div");
div.style.width = "400px;";
div.style.background = "white";
div.style.color = "black";
div.innerHTML = /*"Database LatLng: " + CoordsPush + " Marker Value: " + */markerValueRead;
div.setAttribute("id", CoordsPush);
document.body.appendChild(div);
//alert(div.id);
JSON.stringify(markerValueRead);
xively.setKey("UYby76Zocsur664I6sRd13BXKUKrpM3xDSntN5qB5fvPxMhG");
var feedID = 129375335,
datastreamID = "LatLng";
selector = "50.3754565, -4.14265649999993"
xively.datastream.get(feedID, datastreamID, function(datastream) {
$selector.html( datastream["current_value"] );
xively.datastream.subscribe(feedID, datastreamID, function( even, datastream_updated) {
$(selector).html(datastream_updated["current_value"]);
});
});
//console.log("(" + markers[d].getPosition().d + ", " + markers[d].getPosition().e +")");
console.log("Database LatLng: " + databaseMarkers + " Marker Value: " + markerValueRead);
}
counter = 0;
}
});
document.body.innerHTML = '';
}, 15000);
});
</script>
<script src="http://d23cj0cdvyoxg0.cloudfront.net/xivelyjs-1.0.4.min.js"></script>
</body>
</html>
Here's a simple example of sending a single value to each of two channels ("foo" and "bar") in a feed...
<!DOCTYPE HTML>
<html>
<head>
<title>Xively Test</title>
<script language="JavaScript" type="text/javascript" src="lib/jquery/jquery-1.10.2.min.js"></script>
<script language="JavaScript" type="text/javascript" src="lib/xively/xivelyjs-1.0.4.min.js"></script>
<script language="JavaScript" type="text/javascript">
var API_KEY = "YOUR_API_KEY";
var FEED_ID = "YOUR_FEED_ID";
$(document).ready(function() {
// Set your API key first
xively.setKey(API_KEY);
// build the data packet
var timestamp = new Date().toISOString();
var data = { "version" : "1.0.0",
"datastreams" : [
{ "id" : "foo", "datapoints" : [ {"at" : timestamp, "value" : 10} ] },
{ "id" : "bar", "datapoints" : [ {"at" : timestamp, "value" : 20} ] }
]
};
// upload the data
xively.feed.update(FEED_ID, data, function(response) {
if (response.status == "200") {
console.log("Yay, it worked!: " + JSON.stringify(response, null, 3));
}
else {
console.log("Boo, something went wrong!: " + JSON.stringify(response, null, 3));
}
});
});
</script>
</head>
<body>
Look in the console for output.
</body>
</html>
I'm an absolute newbie with javascript, but I'm just trying to tweak JPlayer to use an XML file for the playlist instead of the hard-coded playlist. So, here's the bit of code that creates the playlist:
//<![CDATA[
$(document).ready(function(){
var Playlist = function(instance, playlist, options) {
var self = this;
this.instance = instance; // String: To associate specific HTML with this playlist
this.playlist = playlist; // Array of Objects: The playlist
this.options = options; // Object: The jPlayer constructor options for this playlist
this.current = 0;
this.cssId = {
jPlayer: "jquery_jplayer_",
interface: "jp_interface_",
playlist: "jp_playlist_"
};
this.cssSelector = {};
$.each(this.cssId, function(entity, id) {
self.cssSelector[entity] = "#" + id + self.instance;
});
if(!this.options.cssSelectorAncestor) {
this.options.cssSelectorAncestor = this.cssSelector.interface;
}
$(this.cssSelector.jPlayer).jPlayer(this.options);
$(this.cssSelector.interface + " .jp-previous").click(function() {
self.playlistPrev();
$(this).blur();
return false;
});
$(this.cssSelector.interface + " .jp-next").click(function() {
self.playlistNext();
$(this).blur();
return false;
});
};
Playlist.prototype = {
displayPlaylist: function() {
var self = this;
$(this.cssSelector.playlist + " ul").empty();
for (i=0; i < this.playlist.length; i++) {
var listItem = (i === this.playlist.length-1) ? "<li class='jp-playlist-last'>" : "<li>";
listItem += "<a href='#' id='" + this.cssId.playlist + this.instance + "_item_" + i +"' tabindex='1'>"+ this.playlist[i].name +"</a>";
// Create links to free media
if(this.playlist[i].free) {
var first = true;
listItem += "<div class='jp-free-media'>(";
$.each(this.playlist[i], function(property,value) {
if($.jPlayer.prototype.format[property]) { // Check property is a media format.
if(first) {
first = false;
} else {
listItem += " | ";
}
listItem += "<a id='" + self.cssId.playlist + self.instance + "_item_" + i + "_" + property + "' href='" + value + "' tabindex='1'>" + property + "</a>";
}
});
listItem += ")</span>";
}
listItem += "</li>";
// Associate playlist items with their media
$(this.cssSelector.playlist + " ul").append(listItem);
$(this.cssSelector.playlist + "_item_" + i).data("index", i).click(function() {
var index = $(this).data("index");
if(self.current !== index) {
self.playlistChange(index);
} else {
$(self.cssSelector.jPlayer).jPlayer("play");
}
$(this).blur();
return false;
});
// Disable free media links to force access via right click
if(this.playlist[i].free) {
$.each(this.playlist[i], function(property,value) {
if($.jPlayer.prototype.format[property]) { // Check property is a media format.
$(self.cssSelector.playlist + "_item_" + i + "_" + property).data("index", i).click(function() {
var index = $(this).data("index");
$(self.cssSelector.playlist + "_item_" + index).click();
$(this).blur();
return false;
});
}
});
}
}
},
playlistInit: function(autoplay) {
if(autoplay) {
this.playlistChange(this.current);
} else {
this.playlistConfig(this.current);
}
},
playlistConfig: function(index) {
$(this.cssSelector.playlist + "_item_" + this.current).removeClass("jp-playlist-current").parent().removeClass("jp-playlist-current");
$(this.cssSelector.playlist + "_item_" + index).addClass("jp-playlist-current").parent().addClass("jp-playlist-current");
this.current = index;
$(this.cssSelector.jPlayer).jPlayer("setMedia", this.playlist[this.current]);
},
playlistChange: function(index) {
this.playlistConfig(index);
$(this.cssSelector.jPlayer).jPlayer("play");
},
playlistNext: function() {
var index = (this.current + 1 < this.playlist.length) ? this.current + 1 : 0;
this.playlistChange(index);
},
playlistPrev: function() {
var index = (this.current - 1 >= 0) ? this.current - 1 : this.playlist.length - 1;
this.playlistChange(index);
}
};
var mediaPlaylist = new Playlist("1", [
{
name:"song1",
mp3: "song1.mp3",
poster: "http://www.jplayer.org/video/poster/Incredibles_Teaser_640x272.png"
},
{
name:"song2",
mp3: "song2.mp3",
poster: "http://www.jplayer.org/video/poster/Incredibles_Teaser_640x272.png"
},
{
name:"song3",
mp3: "song3.mp3",
poster: "http://www.jplayer.org/video/poster/Incredibles_Teaser_640x272.png"**
}
], {
ready: function() {
mediaPlaylist.displayPlaylist();
mediaPlaylist.playlistInit(false); // Parameter is a boolean for autoplay.
},
ended: function() {
mediaPlaylist.playlistNext();
},
swfPath: "js",
supplied: "ogv, m4v, oga, mp3"
});
});
The part that starts with "var mediaPlaylist" is the only section i need to change. Instead of having the keys/values as name: songname, mp3: mp3, etc., I want it to pull these values from an XML file, or better yet, just push them into the array from an XML that looks like:
<song>songname</song>
<mp3>file.mp3</mp3>
The thing that's mostly confusing me here is how that function/array is set up...too many curly braces and brackets to wrap my head around. How do I get into this without breaking it?
If you have a server side XML playlist file in the form:
<?xml version="1.0" encoding="UTF-8"?>
<playlist>
<entry>
<song>songname</song>
<mp3>file.mp3</mp3>
</entry>
<entry>
<song>songname2</song>
<mp3>file2.mp3</mp3>
</entry>
</playlist>
then you can load this from the client via AJAX and create a JSON array from the XML. The following example uses the jQuery JavaScript framework and also the jquery-json plugin (only to format the Array nicely as JSON). You should be able to run this on Chrome or Firefox as both have a console (accessed by pressing F12 in the browser) in which I am outputting the JSON array.
Note: You could change console.log(...) to alert(...) if you don't use either of those browsers.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript" src="http://jquery-json.googlecode.com/files/jquery.json-2.2.min.js"></script>
<script type="text/javascript">
$(function() {
var Playlist = function(instance, playlist, options) {
var self = this;
this.instance = instance;
this.playlist = playlist;
this.options = options;
};
function getPlaylist(callback) {
var songs = new Array();
$.ajax({
dataType:'xml',
url:'songs.xml',
success : function (xml) {
$(xml).find('entry').each(function() {
songs.push({name: $(this).find('song').text(), mp3: $(this).find('mp3').text()});
});
callback(songs);
}
});
}
getPlaylist(function(songs) {
var playlistFromXML = jQuery.toJSON(songs);
var mediaPlaylist = new Playlist('1', playlistFromXML, null);
console.log(mediaPlaylist);
// etc...
});
});
</script>
</head><body></body></html>
Credit to How to return an array from jQuery ajax success function properly? answer as I used that approach to return a value from the jQuery $.ajax success method.
You will have to refactor the var mediaPlaylist = new Playlist() constructor into the callback function though as you cannot return the playlist value from the callback (see How can I catch the return value from the result() callback function that I'm using?)
Hope this helps :-)