Initially, I have the following:
<ul id="my_List">
...
</ul>
Using Javascript, I want to generate "li" elements within that ul block that use values for the text information from the array.
For example, I have a template:
<li>
<div>
<a href="#">
<span class="icon"></span>
<span class="text">[PLACEHOLDER HERE]</span>
<span class="icon"></span>
<span class="icon large"></span>
<br/>
<span class="information">[PLACEHOLDER HERE]</span>
<hr />
</a>
</div>
</li>
and I'm trying to figure out how to parse the information from my Javascript array into the [PLACEHOLDER HERE] blocks while still applying the whole template (all the internal tags and classes) in some kind of for loop. Is there a quick way of doing this or will I have to do a series of appends and whatnot to achieve my goal here?
array mapping is another option
var arr = [
{ a: 'foo', b: 'bar' },
{ a: 'foo 2', b: 'bar 2' },
];
var html = arr.map(function(item) {
return '<li>' +
'<span>' + item.a + '<span>' +
'<span>' + item.a + '<span>' +
'</li>';
}).join('');
document.getElementById('#list').innerHTML = html;
<ul id="#list"></ul>
Tried to create a model. Continue like this and create a model like you want.
<script language="JavaScript">
window.onload = function() {
function createTable(tableData) {
var ul = document.getElementById('my_List');
var li = document.createElement('li');
var div = document.createElement('div');
var ahref = document.createElement('a');
tableData.forEach(function(rowData) {
var row = document.createElement('tr');
rowData.forEach(function(cellData) {
var cell = document.createElement('span');
cell.innerHTML = cellData;
row.appendChild(cell);
});
ahref.appendChild(row);
});
div.appendChild(ahref);
li.appendChild(div);
ul.appendChild(li);
}
createTable([["row 1, cell 1", "row 1, cell 2"], ["row 2, cell 1", "row 2, cell 2"]]);
}
</script>
So you have an HTML element you want to use as a template -- simply clone it and populate as needed.
$("#create-more").on("click", function() {
// First, save references...
var myContentPane = $(".content-pane");
var myText = $(".form-text").val();
var myInfo = $(".form-info").val();
// This is a clone of our hidden template
var myTemplate = $("#template").clone();
// Replace placeholders in the template...
myTemplate.attr("id", "");
myTemplate.find(".text").text(myText);
myTemplate.find(".information").text(myInfo);
// Now we use the template!
myContentPane.append(myTemplate);
});
#template {
display: none;
}
label {
font-weight: bold;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<label for="text-stuff">Some text:</label>
<input type="text" class="form-text" />
<label for="information">Some Info:</label>
<input type="text" class="form-info" />
<button id="create-more">
Create more
</button>
</form>
<div id="template">
<a href="#"><span class="icon" </span>
<span class="text">[PLACEHOLDER HERE]</span>
<span class="icon"></span>
<span class="icon large"></span>
<br/>
<span class="information">[PLACEHOLDER HERE]</span>
<hr />
</a>
</div>
<section class="content-pane">
</section>
A solution to this problem from scratch can get very messy; I'd recommend you make use of available libraries.
lodash has a great solution called _.template. Check out the docs here.
// constructing string like this for readability
var li = [
'<li>',
'<div><%=id%></div>',
'<div><%=name%></div>',
'</li>'
].join('');
var liTemplate = _.template(li);
var liOne = liTemplate({ id: '1', name: 'Hello' }); // => "<li><div>1</div><div>Hello</div></li>"
var liTwo = liTemplate({ id: '2', name: 'World' }); // => "<li><div>2</div><div>World</div></li>"
EDIT: In order to get those strings into your DOM, you could do the following:
var ul = document.getElementById('my_list');
ul.innerHTML = liOne + liTwo;
Related
Using document.getElementsByClassName("span3 pickItem").outerHTML) I set a variable htmlData to contain:
<div itemscope="" class="span3 pickItem">
<p itemprop="name" class="name">
<a href="/user/view?id=4943">
<span>John Doe</span>
<br />
<span>'Arizona'</span>
<br />
<span>'Student'</span>
</a>
</p>
</div>
How can I pick each value from the span tag and console.log them as such:
console.log(...span[0]) output: John Doe
console.log(...span[1]) output: Arizona
console.log(...span[2]) output: Student
Could do something like this
let namesArr = [];
let name = document.querySelectorAll("span");
name.forEach(function(names) {
namesArr.push(names.innerHTML);//Stores all names in array so you can access later
});
console.log(namesArr[0]);
console.log(namesArr[1]);
console.log(namesArr[2]);
Something like this should work:
// console log all names
const items = document.querySelectorAll('div.pickItem span')
items.forEach(item => {
console.log(item.innerText)
})
// console each from array index
console.log(items[0].innerText)
console.log(items[1].innerText)
console.log(items[2].innerText)
let spans = document.getElementsByTagName('span');
console.log(spans[0].innerHTML); //'Joe Doe'
You don't even need the htmlData variable because the DOM elements already exist. If you want to learn about parsing a string of HTML (this is what your htmlData variable has in it) into DOM elements, you can reivew DOMParser.parseFromString() - Web APIs | MDN.
Select the anchor
Select its child spans and map their textContent properties
function getTextFromSpan (span) {
// Just return the text as-is:
// return span.textContent?.trim() ?? '';
// Or, you can also remove the single quotes from the text value if they exist:
const text = span.textContent?.trim() ?? '';
const singleQuote = `'`;
const hasQuotes = text.startsWith(singleQuote) && text.endsWith(singleQuote);
return hasQuotes ? text.slice(1, -1) : text;
}
const anchor = document.querySelector('div.span3.pickItem > p.name > a');
const spanTexts = [...anchor.querySelectorAll(':scope > span')].map(getTextFromSpan);
for (const text of spanTexts) {
console.log(text);
}
<div itemscope="" class="span3 pickItem">
<p itemprop="name" class="name">
<a href="/user/view?id=4943">
<span>John Doe</span>
<br />
<span>'Arizona'</span>
<br />
<span>'Student'</span>
</a>
</p>
</div>
Here is my problem. I have the following function
def index():
rows=db(db.mylist).select()
return dict(rows=rows)
so whenever I reload the the front view index I want to retrieve rows from the database and display the data to the user in a list
{{for(r in rows)}}
li.innerhtml={{=rows.task}}
{{pass}}
Obviously, this is not the right way to do it. I think I have to use json and XML.
This is the table I am using
db.define_table(
'mylist',
Field('task', 'string')
)
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading center " style="color: black; font-style: inherit">
<form>
Task:
<input name="name" id="task" />
<button type="button" class="btn btn-success btn-sm" onclick=add(),ajax('{{=URL('default','insert_task')}}',['name']) >add</button>
</form>
</div>
<div class="panel-body center">
<ul id="list" class="list-group"> </ul>
</div>
</div>
</div>
</div>
var ul = document.getElementById("list");
var lastId=0;
function add()
{
if(!isBlank(document.getElementById("task").value)) {
var iCon = document.createElement('div'); //create a div container
var dbtMenu=document.createElement('div');
var li = document.createElement("il"); //create a list-element
var closeSpan = document.createElement("span"); //create a span for badge attribute
var ClickListState=0;
dbtMenu.setAttribute("class","container");
//dbtMenu.appendChild(dropDownList);
li.setAttribute('id',lastId); // set an attribute for id
iCon.className = "glyphicon glyphicon-remove"; // image for remove button
closeSpan.setAttribute("class", "badge"); //create a new attribute for span
closeSpan.appendChild(iCon); // put it in the span set
iCon.addEventListener("click",function(){var element=document.getElementById(li.getAttribute('id'));
element.parentNode.removeChild(element);}); //functionlity
li.innerHTML = document.getElementById('task').value;
var value=document.getElementById('task').value;
var pass= document.getElementById('task').value;
li.setAttribute("class", "list-group-item hover-yellow");
li.addEventListener('click',function() {if(ClickListState==0){li.style.backgroundColor="red"; ClickListState++;}
else {li.style.backgroundColor="white"; ClickListState--; }});
li.appendChild(closeSpan);
lastId++;
ul.appendChild(li);
}
}
function update()
{
{{for r in rows:}}
var iCon = document.createElement('div'); //create a div container
var dbtMenu = document.createElement('div');
var li = document.createElement("il"); //create a list-element
var closeSpan = document.createElement("span"); //create a span for badge attribute
var ClickListState = 0;
dbtMenu.setAttribute("class", "container");
//dbtMenu.appendChild(dropDownList);
li.setAttribute('id', lastId); // set an attribute for id
iCon.className = "glyphicon glyphicon-remove"; // image for remove button
closeSpan.setAttribute("class", "badge"); //create a new attribute for span
closeSpan.appendChild(iCon); // put it in the span set
iCon.addEventListener("click", function () {
var element = document.getElementById(li.getAttribute('id'));
element.parentNode.removeChild(element);
});
// var t ={#{=XML(response.json(r.task))}}
li.innerHTML = "t";
var value = document.getElementById('task').value;
var pass = document.getElementById('task').value;
li.setAttribute("class", "list-group-item hover-yellow");
li.addEventListener('click', function () {
if (ClickListState == 0) {
li.style.backgroundColor = "red";
ClickListState++;
}
else {
li.style.backgroundColor = "white";
ClickListState--;
}
});
li.appendChild(closeSpan);
lastId++;
ul.appendChild(li);
{{pass}}
}
update();
Read the basic syntax for template language in web2py here
You want this:
<ul>
{{for row in rows:}}
<li>{{=row}}</li>
{{pass}}
</ul>
Other solution can be, build the complete list in controller function using html helpers and pass it to view
def index():
rows = db(db.mylist).select()
my_list = [row.task for row in rows]
task_list = UL(*my_list)
return dict(task_list=task_list)
And in the view just do:
{{=XML(task_list)}}
XML is an object used to encapsulate text that should not be
escaped.
I will suggest you to go through these 2 examples: Image blog and Simple wiki
EDIT:
From your edit, I think you want to add new task using form and wanted to add the list without refreshing the page.
Read Ajax form submission, also related ajax example is given in simple wiki app
<!-- Views/index.html-->
{{extend 'layout.html'}}
<form id="task_form">
Task:
<input name="name" id="task" />
<input type="submit" class="btn btn-success btn-sm" value="Add" />
</form>
<div id="target"> {{=XML(task_list)}}</div>
<script>
jQuery('#task_form').submit(function() {
ajax('{{=URL("default", "insert_task")}}',
['name'], 'target');
return false;
});
</script>
--
# Controller
def index():
rows = db(db.mylist).select()
my_list = [row.task for row in rows]
task_list = UL(*my_list)
return dict(task_list=task_list)
def insert_task():
"""an ajax callback that returns a <ul>"""
task_name = request.vars.name
db.mylist.insert(task=task_name)
rows = db(db.mylist).select()
my_list = [row.task for row in rows]
task_list = UL(*my_list)
return task_list
I came up with this to convert a python list of strings to a javascript array of strings:
{{def print_js_array(left_side, pylist):
wrap_quotes = map(lambda a: "'{}'".format(a), pylist)
comma_separated = "" if not pylist else reduce(lambda a,b: "{}, {}".format(a,b), wrap_quotes)
return "{} = [{}];".format(left_side, comma_separated)
}}
{{=SCRIPT(
""
+ print_js_array("var un_pythoned", status_filter_options))
}}
Which given a python list ['', 'Not Started', 'Running', 'Finished', 'Failed'] results in (html):
<script><!--
var un_pythoned = ['', 'Not Started', 'Running', 'Finished', 'Failed'];
//--></script>
Making the array available to subsequent scripts. You could probably write something similar to print a dictionary as json.
I'm gonna append comments into <ul class="chat" id="comments_section"> with retrieved remote json data
return json data like this :
rtndata = [
{
username: Jordan,
message: 123,
},
{
username: Kobe,
message: 456,
},
]
implement ideas :
rtndata.forEach(function (comment, index) {
if index == EvenNumber:
append_comment_div_with_Even_Number_format;
else :
append_comment_div_with_Odd_Number_format;
});
Finnaly the DOM structure should look like the following,
The attributes left and right should be used interleavely in the comment div template.
Could we use any template technique in purely js lib? (Does any one lib of backbone.js, react.js, underscore.js can do this job elegantly ?)
Thank you.
Expected result
<ul class="chat" id="comments_section">
<li class="left.clearfix">
<span class="pull-left chat-img">
<img src="http://graph.facebook.com/Jordan/picture">
</span>
<span class="pull-left msg">
123
</span>
</li>
<li class="right.clearfix">
<span class="pull-right chat-img">
<img src="http://graph.facebook.com/Kobe/picture">
</span>
<span class="pull-right msg">
456
</span>
</li>
</ul>
By the looks of it, you're trying to adjust the style of alternate elements by adding css classes via js.
You can handle this without js, using css :nth-child selector:
li:nth-child(odd) {
}
li:nth-child(odd) span.msg{
}
li:nth-child(even) {
}
li:nth-child(even) span.msg{
}
If you must add classes (maybe you're using bootstrap), you should be able to do something like the following using underscore's template method:
<ul class="chat" id="comments_section">
<% _.each(comments, function(comment, i) { %>
<li class="<%= i%2==0? 'left' : 'right' %> clearfix">
<span class="pull-<%= i%2==0? 'left' : 'right' %> chat-img">
<img src="http://graph.facebook.com/Kobe/picture">
</span>
<span class="pull-<%= i%2==0? 'left' : 'right' %> msg">
456
</span>
</li>
<% }); %>
</ul>
Here's one approach:
var rtndata = [{
username: 'Jordan',
message: 123,
}, {
username: 'Kobe',
message: 456,
}, ];
var ul = document.getElementById('comments_section');
rtndata.forEach(function(comment, index) {
var even = (index % 2 === 0);
var li = document.createElement('li');
li.className = (even ? 'left.clearfix' : 'right.clearfix');
var span1 = document.createElement('span');
span1.className = (even ? 'pull-left' : 'pull-right') + ' chat-img';
var img = document.createElement('img');
img.src = 'http://graph.facebook.com/' + comment.username + '/picture';
var span2 = document.createElement('span');
span2.className = (even ? 'pull-left' : 'pull-right') + ' msg';
span2.innerHTML = comment.message;
span1.appendChild(img);
li.appendChild(span1);
li.appendChild(span2);
ul.appendChild(li);
});
Output:
Since you don't have that many elements, we can create a few elements and set them. If you have a lot, a second approach would be to create an html template and do a find replace.
I'm using knockoutjs to bind data, and I want to append data bind in one element.
Here is my code :
html:
<div id="wrapper">
<div data-bind="foreach: people">
<h3 data-bind="text: name"></h3>
<p>Credits: <span data-bind="text: credits"></span></p>
</div>
Javascript code:
function getData(pageNumber)
{
//code get data
//binding
ko.applyBindings({ peopla: obj }, document.getElementById('wrapper'));
}
In the first time the pageNumber is 1, then I call getData(1), and I want show more data in page 2 I will call getData(2), and in page 2 data will be show more in wrapper element like append in jquery.
If I use normal jquery I can call some like that
$("#wrapper").append(getData(2));
So I don't know how to use knockout bind more data in one elemnt
Try the following script, this sort of simulates how you can append data to your array by replacing existing data or adding on to it. Hope it helps
function myModel() {
var self = this;
self.people = ko.observableArray([{
name: 'Page 1 data',
credits: 'credits for page 1'
}]);
var i = 2;
self.getData = function () {
var returnedData = [{
name: 'Page ' + i + ' Name',
credits: 'credits for page ' + i
}];
self.people(returnedData);
i++;
}
self.getData2 = function () {
var returnedData = {
name: 'Page ' + i + ' Name',
credits: 'credits for page ' + i
};
self.people.push(returnedData);
i++;
}
}
ko.applyBindings(new myModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="wrapper">
<div data-bind="foreach: people">
<h3 data-bind="text: name"></h3>
<p>Credits: <span data-bind="text: credits"></span>
</p>
</div>
<button data-bind="click: getData">Simulate get data (Replaces current data)</button>
<button data-bind="click: getData2">Append to the same array (Adds to existing array)</button>
HTML source:
<span class="specLink">
<specialty><a title="Plastic Surgery" href="link2.aspx">Plastic Surgery</a></specialty>
</span>
<br />
<span class="specLink">
<specialty2><a title="Hand Surgery" href="link3.aspx">Hand Surgery</a></specialty2>
</span>
How can I create a JQuery script which runs during page load to displays the same list taking from the HTML Source listed above?
E.g.:
<div class="justPad">
<a title="Plastic Surgery" href="link2.aspx" class="defaultLinks">Plastic Surgery</a>
</div>
<div class="justPad">
<a title="Hand Surgery" href="link3.aspx" class="defaultLinks">Hand Surgery</a>
</div>
How I would like it to be:
var k = "";
$(".specLink").each(function() {
var aLink = $(".specLink").replace(<%-- Remove the <specialty#></specialty#> tags and only keep the anchor link --%>);
k += '<div class="justPad">'; //.. as many entries that shows up
k += aLink; //.. as many entries that shows up
k += '</div>'; //.. as many entries that shows up
});
//Once I have added
$(".addSpecialties").html(k);
Blank HTML:
<div class="serviceHolder brClear addSpecialties">
//add inside here from the JQuery above
</div>
Something like:
var specialties = $(".specLink a").map(function() {
return $('<div class="justPad">').append( $(this).clone() )[0];
}).toArray();
$(".addSpecialties").empty().append(specialties);