How to fully create HTML from a js object? - javascript

I am working on finding a way to use javascript to create blog posts for my website. I am quite knowledgeable in javascript, however I haven't used many DOM elements other than document.getElementById(). I am looking for support in how to implement a way to turn a JS object into a post. Here is an example object:
var posts = { //My object
firstPost: { //An example post
index: 1, //Identifies order of posts: 1 is oldest... >1 newest
id: "first", //An id for the post
date: { //Date will be listed next to name on post
month: 11,
day: 2,
year: 2018
},
name: "My Post", //Name of the post
text: "Text for the post...", //Actual Post
image: 'blogImage.png' //An image for the post
}
And now I want to pass it through a function as shown below to create HTML:
function assemblePost( index, id, month, day, year, text, image) {
//DOM GOES IN HERE
}
Here is an example of how the HTML looks when I type it manually:
<div class="card" id="first"> <!-- Card element links to css -->
<h2>My Post</h2> <!-- Name of the post -->
<h5>Posted November 2nd, 2018</h5> <!-- Date of the post -->
<div class="img" style="height:200px;"><img src="/blogImage.png" ></div>
<p>Text for the post..."</p> <!-- Actual Post -->
</div>
I am not entirely sure how to approach this because:
The class, "card", links to the CSS, and I am not sure how to implement that with DOM.
I am not sure if DOM can edit style, as in this example, the image height is 200, but in another image it could be different.
For many of my other posts, I may have multiple paragraphs and/or lists. At the very least, I wanted a way to create one string of text. Maybe for lists, an array would be most useful in my object, however I am unsure how to address multiple paragraphs.
I realize it is a lot of explaining, examples, and guidelines, but I really do appreciate your help! Thank you!

Just do it step by step.
var posts = [ //My object (array of posts)
{ //An example post
index: 1, //Identifies order of posts: 1 is oldest... >1 newest
id: "first", //An id for the post
date: { //Date will be listed next to name on post
month: 11,
day: 2,
year: 2018
},
name: "My Post", //Name of the post
text: "Text for the post...", //Actual Post
image: 'blogImage.png' //An image for the post
},
{ //An example post
index: 2, //Identifies order of posts: 1 is oldest... >1 newest
id: "first", //An id for the post
date: { //Date will be listed next to name on post
month: 11,
day: 2,
year: 2018
},
name: "My another Post", //Name of the post
text: "Text for another post...", //Actual Post
image: 'blogImage.png' //An image for the post
}
];
const container = document.getElementById('div-posts');
posts.forEach(function(post) {
let div = document.createElement('div');
div.className = 'card';
div.id = 'post_' + post.index; //or post.id provided itis unique
//create more elements instead of ".innerHTML" if you wish
div.innerHTML = '<h2>' + post.name + '</h2>' +
'<h5>' + (new Date(post.date.year, post.date.month, post.date.day).toDateString()) + '</h5>' +
'<div class="img"><img src="/' + post.image + '" ></div>' +
'<p>' + post.text + '</p>';
container.appendChild(div);
});
.card {
border: solid 1px #ccc;
width: 400px
}
.img img {
max-width: 100%
}
<div id="div-posts"></div>

Related

jQuery/JavaScript preventing duplicate appends

seemingly minor issue I'm having with a mock-forum system I'm trying to prototype, as the following array and table function I've created duplicates a forum table the screen due to the appends I've used, and I'm not sure how to go about avoiding this or amending this (I.e, if the user repetitively clicks the forum page, the forum will repetitively generate). I'm still fairly knew to the language, but from what I've found from similar posts, everything I tried to apply hasn't resolved the issue.
var index;
var topics = [
{title: "Football Topics", followers: 280, articles: 5, posts: [
{postId: 101, contents: "Dummy content", author: "Dummy content", replies:[
{replyId: 1, contents: "Dummy content", author: "Dummy content"},
{replyId: 2, contents: "Dummy content", author: "Dummy content"},
{replyId: 3, contents: "Dummy content", author: "Dummy content"}
]},
]}
];
var topicTable = $("<table class='forumTable'><tr><th>Title</th><th>Posts</th><th>Followers</th></tr></table>");
//console test
//console.log(topics);
//Looping all topics in variable "topics"
for (index in topics) {
//console test
console.log(topics[index].title);
var row = $("<tr></tr>");
row.append("<td>" + topics[index].title + "</td>");
row.append("<td>" + topics[index].articles + "</td>");
row.append("<td>" + topics[index].followers + "</td>");
topicOnClick(row, topics[index]);
topicTable.append(row);
}
page.append(topicTable);
Based on what you've given I think there are a couple of options.
Option 1
You say if the use repetitively 'clicks' the forum page implying there's a click handler. You can use .one which executes the function at most on times.
var table = "<table><tr><td>column1</td><td>column2</td></tr></table>";
$('button').one('click', function() {
$('#tableContainer').append(table);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="tableContainer">
</div>
<button>add</button>
</body>
Option 2
You could target the container div and use .html() this will allow the function to run on each click but will write over the content so it gives the appearance of only being loaded once.
var table = "<table><tr><td>column 1</td><td>column 2</td></tr></table>";
$('button').on('click', function(){
$('#tableContainer').html(table);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="tableContainer">
</div>
<button>add</button>
</body>

Data Insertion From A Text File Into A Complex Table Layout (HTML, PHP, JS)

I'm building a webpage that utilizes a fairly complex table layout which will hold a decent amount of data. I don't want to hardcode all this data in, so I would like to read it in from a text file and dynamically create the webpage that way. The following is an example I have come up with to illustrate what I am trying to accomplish.
// imagine this as the basket that holds all the groceries
<table id=”basket”>
// this is the label for a new section of the basket (like fruits, or vegetables)
<thead>Fruits</thead>
// this is the section of the basket for the items described by the label above
<tbody>
<tr>
<td>
// this is the container of these items
<table class="category">
// a new section of the container for each item
<tr>
<td>
// information about this particular item
<table class="Item">
<tr><td>Strawberry</td></tr>
<tr><td>Red</td></tr>
<tr><td>Seeds</td></tr>
</table>
</td>
// verbose description about this item
<td>Strawberries are sweet and grow in the summer</td>
</tr>
So if I had data like the following:
Fruits
strawberry, red, seeds, Strawberries are sweet and grow in the summer
blueberry, blue, seeds, Willy Wonka likes these
avocado, green, pit, Still not sure how I feel about these
Vegetables
Celery, green, stalk, A top tier vegetable
Radish, Red, bulb, I still haven't warmed up to these
Potato, Brown, root, A classic!
Underneath the basket table I would have two instances of this code, one for fruits and one for vegetables
// this is the label for a new section of the basket (like fruits, or vegetables)
<thead>Fruits</thead>
// this is the section of the basket for the items described by the label above
<tbody>
<tr>
<td>
// this is the container of these items
<table class="category">
and within each of those sections I would have however many instances of this code as called for, (in this case, 3 for each because there are 3 fruits and 3 vegetables listed)
// a new section of the container for each item
<tr>
<td>
// information about this particular item
<table class="Item">
<tr><td>Strawberry</td></tr>
<tr><td>Red</td></tr>
<tr><td>Seeds</td></tr>
</table>
</td>
// verbose description about this item
<td>Strawberries are sweet and grow in the summer</td>
</tr>
So my ultimate question is,
What is the best way for me to structure a text file in order to accomplish this
and
With what kind of PHP or JavaScript could I successfully read this properly formatted text file, and then generate the HTML I want that contains the correct data
Any advice or insight would be appreciated, thank you.
Okay personally I'd recommend saving your data in json format which is a pretty standard way to represent this type of data. You could then supply it via AJAX or save it in a file and include it with a script tag. Really all depends on your requirements.
Also I don't know what your UI requirements are but I'd personally not create nested tables like this. I would use div tags and write custom css to get the layout I wanted.
// This could be supplied via AJAX or you could save it in a javascript or json file.
// It really depends on your requirements.
var jsonData = [
{
name: "Fruits",
items: [
{
name: "Strawberry",
color: "red",
type: "seeds",
description: "Strawberries are sweet and grow in the summer"
},
{
name: "Blueberry",
color: "blue",
type: "seeds",
description: "Willy Wonka likes these"
},
{
name: "Avocado",
color: "green",
type: "pit",
description: "Still not sure how I feel about these"
}
]
},
{
name: "Vegetables",
items: [
{
name: "Celery",
color: "green",
type: "stalk",
description: "A top tier vegetable"
},
{
name: "Radish",
color: "red",
type: "bulb",
description: "I still haven't warmed up to these"
},
{
name: "Potato",
color: "brown",
type: "root",
description: "A classic!"
}
]
}
]
// This is the javascript code that would read your json data and build HTML from it
document.getElementById("basket").innerHTML = getBasketHTML(jsonData)
function getBasketHTML(data) {
// loop through data
var HTML = ""
for (var i = 0, i_end = data.length; i < i_end; i++) {
var category = data[i]
// add new row and table for each category
HTML += "<tr><table>"
// add category label
HTML += "<thead>" + category.name + "</thead>"
// add category table
HTML += "<tbody><tr><td><table class='category'>"
// loop through category items and build HTML
for (var j = 0, j_end = category.items.length; j < j_end; j++) {
HTML += getItemHTML(category.items[j])
}
// close category table
HTML += "</table></td></tr></tbody></table></tr>"
}
return HTML
}
function getItemHTML(item) {
// opening row tag
var HTML = "<tr>"
// add item information
HTML += "<td><table class='Item'>"
HTML += "<tr><td>" + item.name + "</td></tr>"
HTML += "<tr><td>" + item.color + "</td></tr>"
HTML += "<tr><td>" + item.type + "</td></tr>"
HTML += "</table></td>"
// add verbose description
HTML += "<td>" + item.description + "</td>"
// closing row tag
HTML += "</tr>"
return HTML
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<table id="basket"></table>
</body>
</html>

Javascript: If then statement not working in function

I'm building a personality-type quiz and having difficulty tailoring the results page as I'd like it to. The if statement, contained in the display_scenario function, isn't working. I want it to generate a different final message depending on the value of the countValue variable but it's not working. If you are able to help me understand why it's not working, I'd appreciate it.
The key pieces of code are below. It's also in place on Codepen: http://codepen.io/msummers40/pen/eZaePe
Essentially, the quiz:
- asks people a series of questions
- each answer has a numeric weight assigned to it
- this numeric weight is tallied in the countValue variable
- I want to use the countValue variable as a way to generate one of four final pages
I've tried to set up an if statement at the end of the quiz but it's just not working and I'm unsure of how to get this to work - it seems to be building the final view regardless of what's in the if statement.
If you are able to help, thank you.
<body>
<!-- QUIZ BEGINS -->
<div id="wrapper">
<h2 id="cyoaHeader">TITLE HERE<br><small>SUBHED</small></h2>
<div id="quizImage"> </div>
<div id="prompt"> </div>
<div id="options"> </div>
<div id="buildingStatus"> </div>
</div>
<div id="addLinks"> </div>
<!-- QUIZ ENDS -->
<script>
// JSON for quiz
// Contains the questions, images and variable to give answers weight/direction
var story = {
intro: {
prompt: 'You need to get up and out and get on with a big task - what song would propel you out of bed?',
quizImage: '<img id="quizImage" src="https://upload.wikimedia.org/wikipedia/commons/a/ab/Semi-nude_woman_getting_out_of_bed_(rbm-QP301M8-1887-265a~5).jpg"><br />',
options: [{
name: 'Song 4',
path: 'questionTwo',
addition: 4
}, {
name: 'Song 3',
path: 'questionTwo',
addition: 2
}, {
name: 'Song 2',
path: 'questionTwo',
addition: 3
}, {
name: 'Song 1',
path: 'questionTwo',
addition: 1
}]
},
questionTwo: {
prompt: 'Sentence.<br/><br/>Question 2?',
quizImage: '<img id="quizImage" src="http://www.allgifs.com/wp-content/uploads/2013/09/pancake.gif">',
options: [{
name: 'A',
path: 'result',
addition: 4
}, {
name: 'B',
path: 'result',
addition: 3
}, {
name: 'C',
path: 'result',
addition: 2
}, {
name: 'D',
path: 'result',
addition: 1
}]
},
result: {
prompt: 'This is the default statement on the results page. I need to make it tailored to the countValue variable',
quizImage: '<img id="quizImage" src="http://viralgifs.com/wp-content/uploads/2014/03/cat_did_u_forget.gif">',
options: [{
name: 'FINAL CTA',
path: 'intro',
addition: 0
}]
}
};
//establishing counter for weighted answer
var countValue = 0;
console.log(countValue);
var additionInt = 0;
console.log(additionInt);
// Option is an object with properties {name and path objects, addition array}
function display_scenario(options) { //value passed had been chosen_options
var option_name = options.name;
var option_path = options.path;
var additionInt = options.addition;
countValue += additionInt;
// countValue = (additionInt + countValue);
console.log(additionInt);
console.log(countValue);
var scenario = story[option_path];
// Clear the #prompt, #quizImage, #options, #addLinks, #buildingStatus divs before writing new ones in if statment below -- GROUP THESE IN A FUNCTION BEFORE GO LIVE
jQuery('#prompt').empty();
jQuery('#quizImage').empty();
jQuery('#options').empty();
jQuery('#addLinks').empty();
jQuery('#buildingStatus').empty();
// THIS IF ELSE STRUCTURE DOESN'T SEEM TO BE WORKING - I'M TRYING TO SET IT UP SO THAT IT DISPLAYS ONE OF FOUR OPTIONS, DEPENDING ON THE VALUE OF THE countValue VARIABLE. ATM ONLY THREE OPTIONS ARE IN PLACE; NONE ARE WORKING.
// Create a <p> to display what the user has chosen if option_name is not null and append it to the #prompt <div>
if (options.name != 'result') {
jQuery('<p>').html('<i>You selected <b>' + option_name + '</b></i><br><p>And the running total is ' + countValue + '</p>').appendTo('#buildingStatus');
// Append the scenario's prompt
jQuery('<p>').html(scenario.prompt).appendTo('#prompt');
// Appending an image
jQuery('<p>').html(scenario.quizImage).appendTo('#quizImage');
// Appending links in the addLinks div
jQuery('<div>').html(scenario.addLinks).appendTo('#addLinks');
} else if (options.name == 'result' && countValue<17) {
jQuery('<p>').html('<i>Yay! This option worked. You selected <b>' + option_name + '</b></i><br><p>And the running total is ' + countValue + '</p>').appendTo('#buildingStatus');
// Append the scenario's prompt
jQuery('<p>').html(scenario.prompt).appendTo('#prompt');
// Appending an image
jQuery('<p>').html(scenario.quizImage).appendTo('#quizImage');
// Appending links in the addLinks div
jQuery('<div>').html(scenario.addLinks).appendTo('#addLinks');
} else {
jQuery('<p>').html('<i>Yay! This option worked. You selected <b>' + option_name + '</b></i><br><p>And the running total is ' + countValue + '</p>').appendTo('#buildingStatus');
// Append the scenario's prompt
jQuery('<p>').html(scenario.prompt).appendTo('#prompt');
// Appending an image
jQuery('<p>').html(scenario.quizImage).appendTo('#quizImage');
// Appending links in the addLinks div
jQuery('<div>').html(scenario.addLinks).appendTo('#addLinks');
};
// Append the options into the #options <div>
// We want to loop through all the options and create buttons for each one. A regular for-loop would not suffice because adding a button is not asynchronous. We will create an asynchronous loop by using recursion
function add_option_button(index) {
if (index === scenario.options.length) {
// Base case
return;
}
var option = scenario.options[index];
// Create a <button> for this option and append it to the #options <div>
jQuery('<button>')
.html(option.name)
.click(function(e) {
// This is an onclick handler function. It decides what to do after the user has clicked on the button.
// First, prevent any default thing that the button is going to do, since we're specifying our own action for the button
e.preventDefault();
// We'll want to call display_scenario() with this option
display_scenario(option);
})
.appendTo('#options');
// Add the next option button
add_option_button(index + 1);
}
add_option_button(0);
}
// This function waits until the document is ready
jQuery(document).ready(function() {
// Start the quiz
display_scenario({
name: null,
path: 'intro',
addition: 0
});
});
</script>
</body>
</html>
I think you need to recalculate the sum whenever the user changes something, apart from displaying the scenarios everytime. Split the display_scenario function into one to display the options, and into separate logic to sum up all options choosen.

Use AJAX to load content to a jQuery UI dialog

On my site, I have a bunch of profile previews that are being generated by running JSON data through a mustache.js template.
Here's the template:
<script id="profile-preview-template" type="text/template">
<div class="col-sm-3">
<a style="display:block">
<div class="profile-preview">
<img class="img-responsive img-circle center-block" width="200px" src="{{img_url}}" alt="Photo of {{first_name}} {{last_name}}" />
<h1>{{first_name}} {{last_name}}</h1>
<h2 class="text-muted">{{major}}</h2>
<h3 class="text-muted">Cohort {{cohort}}</h3>
</div>
</a>
</div>
</script>
The profile preview shows select info from the JSON info like first_name, last_name, etc.
Here's the format of the JSON data:
{
"profiles": [
{
"first_name": "Robert",
"last_name": "Hosking",
"img_url": "img/about/hackbert.jpg",
"major": "Computer Science",
"cohort": 12,
"bio": "some info",
"linkedin": "some url",
"home_town": "some place",
"status": "Student"
},
{
"first_name": "Jimmy",
"last_name": "Harval",
"img_url": "img/about/hackbert.jpg",
"major": "Chemistry",
"cohort": 13,
"bio": "some info",
"linkedin": "some url",
"home_town": "some place",
"status": "Student"
}
]
}
However, when one of the previews is clicked, I'd like to make a jQuery UI dialog modal popup containing all of the info in the JSON data (not just the data displayed in the preview).
My question is how do I get a reference as to which profile preview was clicked so I know where in the JSON file to look to get the rest of the information.
I made some slight changes to your HTML and template script.
<div class="profile-full"></div>
<div class="preview"></div>
<script id="profile-preview-template" type="text/template">
<div class="col-sm-3">
<a style="display:block">
<div class="profile-preview">
{{#profiles}}
<img class="img-responsive img-circle center-block" width="200px" src="{{img_url}}" alt="Photo of {{first_name}} {{last_name}}" />
<h1 id="whichProfile">{{first_name}} {{last_name}}</h1>
<h2 class="text-muted">{{major}}</h2>
<h3 class="text-muted">Cohort {{cohort}}</h3>
{{/profiles}}
</div>
</a>
</div>
</script>
As you can see, 2 divs for demonstration purposes.
.profile-full Where the contents of the correct object is output using JSON stringify. What gets output can of course be changed, this is just to show that the correct object is selected.
.preview Where the preview profiles are generated.
I also added the opening and closing {{#profiles}} {{/profiles}}
profile is derived from what is set in the beginning of your json.
"profiles": [
We assume that you have correctly linked the jQuery and mustache.js libraries. Name your json file from your post as data.json
You must also link the .js file below. I have named it main.js.
$(function() {
$.getJSON('data.json', function(data) {
var template = $('#profile-preview-template').html();
var html = Mustache.to_html(template, data);
$('.preview').html(html);
allProfiles = data.profiles;
lookup = [];
for(i=0;i<data.profiles.length;i++) {
lookup.push(data.profiles[i].last_name);
};
});//getJSON
$(document).on('click', '#whichProfile', function() {
var whichName = $(this).text().substr($(this).text().indexOf(" ") + 1);
var i = jQuery.inArray(whichName, lookup);
$('.profile-full').html(JSON.stringify(allProfiles[i]));
});
});//document ready
So what's happening here?
$(function() {
$.getJSON('data.json', function(data) {
var template = $('#profile-preview-template').html();
var html = Mustache.to_html(template, data);
$('.preview').html(html);
After our document.ready shorthand, we GET the contents of the data.json, and hold it in data. Then we put the contents of html into the div with a class of preview, as we have formatted it according to the template script.
allProfiles = data.profiles;
lookup = [];
for(i=0;i<data.profiles.length;i++) {
lookup.push(data.profiles[i].last_name);
};
});//getJSON
Next we'll create an array called lookup which contains only the last_name data from our json. This will make it possible and fast to get the reference as to which profile preview was clicked.
If you only have a few names, using the last name is fine, but use a unique identifier if the amount of profiles grows (So overlapping last names doesn't occur). Consider adding a unique id, or email to your json.data.
Then close your getJSON.
$(document).on('click', '#whichProfile', function() {
var whichName = $(this).text().substr($(this).text().indexOf(" ") + 1);
var i = jQuery.inArray(whichName, lookup);
$('.profile-full').html(JSON.stringify(allProfiles[i]));
});
});//document ready
Here on clicking a name will get the .text of what's on our page. We will convert it so it's only the last name (strip everything before the space).
We then search for this last name within the lookup array and return its index. Now that we have this index we can use it to get any value within the json relative to the index. In this example we just put the entire stringified contents of the relevant object into our profile-full div
Finally we close our document.ready.
Try clicking on a name (Can be the first or last name). We could of course put anything from that relevant object anywhere else. For example:
remove the line
$('.profile-full').html(JSON.stringify(allProfiles[i]));
and replace it with
alert(allProfiles[i].first_name + ' ' + allProfiles[i].last_name +
' is studying ' + allProfiles[i].major + ' as their Major');
Now clicking on a name will get a pop up, stating their full name and which Major they are studying.
In your template, give each <a> element an id attribute equal to the index of the corresponding profile in the JSON array.
Then, you can give the <a> element an onclick method such as foo(this), because this will pass the element to the function. In your function, you can retrieve the id with $(elem).attr("id"), where elem is the function parameter. Now you have the index of the profile, so it should be easy to retrieve the profile's information.

JQuery Cascade plugin to remember last selected state

I'm using the JQuery cascade plugin (here)
I have it so I can select a book section in the first dropdown, which populates with chapters in the second drop down. e.g:
var list1 = [
{'When':'0','Value':'0','Text':'Chapter 1'},
{'When':'0','Value':'1','Text':'Chapter 2'},
{'When':'0','Value':'2','Text':'Chapter 3'},
...(snip..)
{'When':'1','Value':'0','Text':'Appendix 1'},
{'When':'1','Value':'1','Text':'Appendix 2'},
{'When':'1','Value':'2','Text':'Appendix 3'},
....(snip..)
];
function commonTemplate(item){
return "<option value='" + item.Value + "'>" + item.Text + "</option>";
};
function commonTemplate2(item) {
return "<option value='" + item.Value + "'>***" + item.Text + "***</option>";
};
function commonMatch(selectedValue) {
return this.When == selectedValue;
};
Which is then called by:
jQuery(document).ready(function()
{
jQuery("#section").cascade("#chapter",{
list: list1,
template: commonTemplate,
match: commonMatch
});
});
my form is pre-populated with section0 and it's associated chapters. Everything works fine - when I select book 2, the script fires and populates the second drop down with the chapters from book 2 etc.
My problem is: When I select a book like "Section 2, Chapter 3", I would like that book and chapter to load while the form on the page displays "Section 2, Chapter 3". This way the user can quickly go to chapter 4 or whatever. Currently, when Section 2, Chapter 3 loads, the form defaults to "Section 1, Chapter 1" which is the default form value from the first time the user arrived at the page.
My Question is: How can I have a default value the first time, but when a user submits the form, the page reloads remembering the last selected section/chapter in the form?
Thanks.

Categories