I have some HTML in my DOM and I want to replace some strings in it, but only if that was not already replaced or that is not a TAG.
All that is based on an Array that contains the string I want to find and the new string I want this to be replace with.
Work in progress: https://jsfiddle.net/u2Lyaab1/23/
UPDATE: The HTML markup is just for simplicity written with ULs in the sample code, BUT it can contain different tags, event different nesting levels
Basically the desiredReplcement works nice (except that it looks in tags too), but I want that to happen on the DOM, not the new string because I want to maintain any other HTML markup in the DOM.
SNIPPET:
var list = [{
original: 'This is',
new: 'New this is'
},
{
original: 'A list',
new: 'New A list'
},
{
original: 'And I want',
new: 'New And I want'
},
{
original: 'To wrap',
new: 'New To wrap'
},
{
original: 'li',
new: 'bold'
},
{
original: 'This',
new: 'New This'
},
{
original: 'strong',
new: 'bold'
}, {
original: 'This is another random tag',
new: 'This is another random tag that should be bold'
}
];
var div = $('.wrap');
var htmlString = div.html();
var index = 0;
list.forEach(function(item, index) {
console.log(index + ' Should replace: "' + item.original + '" with "' + item.new + '"');
//I know that there is something here, but not sure what
index = htmlString.indexOf(item.original);
var expressionLength = index + item.original.length;
var substring = htmlString.substring(index, expressionLength);
var desiredReplcement = substring.replace(item.original, '<strong>' + item.new + '</strong>');
console.log('index', index);
console.log('substring', substring);
console.log('desiredReplcement', desiredReplcement);
//Current implementation in replace looks in the full div, but I just want to replace in the substring mathced above;
var replacement = '<strong>' + item.new + '</strong>';
var newHTML = div.html().replace(item.original, replacement);
div.html(newHTML);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<ul>
<li>This is</li>
<li>A list</li>
<li>And I want</li>
<li>This should not be bold</li>
<li>To wrap</li>
<li>This</li>
<li>strong</li>
<li>li</li>
</ul>
<span><p><em>This is another random tag</em></p></span>
</div>
Your div variable is referencing <div class="wrap">...</div>, therefore your htmlString value is a group of html tags instead of string.
That is the main reason your code is not working as expected.
And therefore I rewrote your implementation.
var list = [
{
original: 'This is',
new: 'New this is'
},
{
original: 'A list',
new: 'New A list'
},
{
original: 'And I want',
new: 'New And I want'
},
{
original: 'To wrap',
new: 'New To wrap'
},
{
original: 'li',
new: 'bold'
},
{
original: 'This',
new: 'New This'
},
{
original: 'strong',
new: 'bold'
}
];
var div = document.getElementsByClassName('wrap')[0].getElementsByTagName('li'); // Getting all <li> elements within <div class="wrap">
Array.prototype.forEach.call(div, function(li, x){ // Borrowing Array's forEach method to be used on HTMLCollection
list.forEach(function(value, i){ // Looping through list
if (value.original === li.innerHTML) // if list[i]['original'] === li[x].innerHTML
li.innerHTML = '<strong>' + value.new + '</strong>';
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<ul>
<li>This is</li>
<li>A list</li>
<li>And I want</li>
<li>This should not be bold</li>
<li>To wrap</li>
<li>This</li>
<li>strong</li>
<li>li</li>
</ul>
</div>
I don't think that jQuery is necessary here.
First, you want to retrieve your container, which in your case will be the .wrap div.
var container = document.querySelector('.wrap');
Then you want to create a recursive function that will loop through an array to search and replace the data provided.
function replacement(containers, data){
if(!data || !data.length)
return;
for(let i=0; i<containers.length; i++){
var container = containers[i];
// Trigger the recursion on the childrens of the current container
if(container.children.length)
replacement(container.children, data);
// Perform the replacement on the actual container
for(let j=0; j<data.length; j++){
var index = container.textContent.indexOf(data[j].original);
// Data not found
if(index === -1)
continue;
// Remove the data from the list
var replace = data.splice(j, 1)[0];
container.innerHTML = container.innerHTML.replace(replace.original, '<strong>' + replace.new + '</strong>');
// Lower the j by 1 since the data array length has been updated
j--;
// Only want to perform one rule
break;
}
}
}
Demo: https://jsfiddle.net/u2Lyaab1/25/
The following code will not replace tags and will do only one replacement for one text node (if there is any match). It looks through the whole structure in a recursive manner and checks the text of the elements.(and it uses the same list you described in your question)
Requirements:
Replace text just in case of exact match => use === instead of indexOf
Replace text only once => remove item from list after use
var div = $('.wrap');
function substitute(htmlElement, substituteStrings){
var childrenElements = htmlElement.children;
if(childrenElements.length !== 0){
for (let i=0;i<childrenElements.length;i++){
substitute(childrenElements[i], substituteStrings);
}
} else {
var htmlString = htmlElement.innerText;
substituteStrings.some(function(item){
if(htmlString == item.original){
htmlElement.innerHTML = htmlString.replace(item.original, '<strong>' + item.new + '</strong>');
substituteStrings.splice(index,1);
return true;
}
});
}
}
substitute(div[0],list);
The basic idea is to use recursion to search through every nested node in the parent node.
My answer (partial answer) has the same results as Zsolt V's, but is a little less elegant.
Zsolt V has checked child nodes, and it can therefore work with innerHTML by using HTML tags. I on the other hand have checked if a node is a textNode, and have built the replacement nodes using the DOM (pure DOM solution) and nodes' textContent property.
var list = [{
original: 'This is',
new: 'New this is'
}, {
original: 'A list',
new: 'New A list'
}, {
original: 'And I want',
new: 'New And I want'
}, {
original: 'To wrap',
new: 'New To wrap'
}, {
original: 'li',
new: 'bold'
}, {
original: 'This',
new: 'New This'
}, {
original: 'strong',
new: 'bold'
}, {
original: 'This is another random tag',
new: 'This is another random tag that should be bold'
}
];
//I want for each expression in this array, to find that expression in array, replace-it and make-it bold with a <strong> tag.
var div = document.getElementsByClassName("wrap")[0];
function processNode(node) {
if (node.nodeName === "#text") {
list.forEach(function(item, index) {
if (node.parentNode && node.textContent.indexOf(item.original) > -1) {
//node.textContent = node.textContent.replace(item.original, item.new);
let untouched = node.textContent.split(item.original);
console.log(untouched);
for (let i = untouched.length - 1; i > 0; i--) {
untouched.splice(i, 0, item.new);
}
console.log(untouched);
for (let i = 0, l = untouched.length; i < l; i++) {
let newNode = i % 2 === 0 ? document.createTextNode("") : document.createElement("strong");
newNode.textContent = untouched[i];
node.parentNode.appendChild(newNode);
}
node.parentNode.removeChild(node);
}
})
} else {
node.childNodes.forEach(function(child, index) {
processNode(child);
})
}
}
processNode(div)
JSFiddle (partial answer)
You write in the comments on Zsolt V's answer that:
but as you can see, the last sentence is replaced differently than the expected in the list
However, the problem is not with the code, but with the ordering of the list array. The problem is that you have replacements that work within one another, i.e. acting on list[7], with list[0]:
"This is another random tag" (list[7] before)
-> "New this is another random tag" (list[7] after applying changes from list[0])
You need to be mindful of the ordering.
In fact, I moved the last item in the list array to the top, and the results are as you've asked for.
var list = [{
original: 'This is another random tag',
new: 'This is another random tag that should be bold'
}, {
original: 'This is',
new: 'New this is'
}, {
original: 'A list',
new: 'New A list'
}, {
original: 'And I want',
new: 'New And I want'
}, {
original: 'To wrap',
new: 'New To wrap'
}, {
original: 'li',
new: 'bold'
}, {
original: 'This',
new: 'New This'
}, {
original: 'strong',
new: 'bold'
}
];
JSFiddle (full answer)
Related
I'm trying to add a bootstrap card inside a div called [itemscontainer] using javascript
by document.getElementById("itemscontainer").innerHTML so i want the cards to be inserted inside the itemscontainer only one time like this :-
but the problem is the items cards keeps reapet them salves more than one time like:-
what i want is to clear the itemscontainer first before adding the cards and this is what i have tried so that the items will be only one cards for each item
// clear function
function clear(){
document.getElementById("ssst").innerHTML = ""
}
// listener append all items to the inventory
window.addEventListener('message', (event) => {
let data = event.data
if(data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
if(document.getElementById("ssst").innerHTML == ""){
clear()
}else{
document.getElementById("ssst").innerHTML +=
"<div class='card holder'>"+
'<div class="card-body">'+
'<img src="icons\\'+icon+'" style="position:absolute;left:15%;width:40px; height:36px;" alt="">'+
'<h4 id="counter">'+count+'</h4>'+
'</div>'+
'<span class="itemname">'+name+'</span>'+
'</div>";'
}
}
})
The real solution is to figure out why you are getting the items more than once. With the information you provided that is impossible for me to answer. So the only thing we can recommend is how to prevent items from being added more than once.
If your messaging system returns duplicates you can determine if you have seen it. If you do, replace it. Otherwise add it.
window.addEventListener('message', (event) => {
const data = event.data;
console.log(data)
if (data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
const html = `
<div class='card holder' data-name="${name}">
<div class="card-body">
<img src="icons/${icon}" style="position:absolute;left:15%;width:40px; height:36px;" alt="${icon}">
<h4 id="counter">${count}</h4>
</div>
<span class="itemname">${name}</span>
</div>`;
const elemExists = document.querySelector(`[data-name="${name}"]`);
if (elemExists) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
elemExists.replaceWith(doc.body);
} else {
document.getElementById("ssst").insertAdjacentHTML("beforeend", html);
}
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1,
icon: 'foo'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'bar',
count: 40,
icon: 'barrrrrr'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1000,
icon: 'foo'
}
});
<div id="ssst"></div>
Why are you using the if statement, what are you checking for?
remove the if statement, I can't see the reason for it to be used here.
clear()
and the rest of your code.
I'm looping through all the html tags in an html-file, checking if those tags match conditions, and trying to compose a JSON-object of a following schema:
[
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' },
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' },
{ title: 'abc', date: '10.10.10', body: ' P tags here', href: '' }
]
But I'd like to create the new entry only for elements, classed "header", all the other elements have to be added to earlier created entry. How do I achieve that?
Current code:
$('*').each((index, element) => {
if ( $(element).hasClass( "header" ) ) {
jsonObject.push({
title: $(element).text()
});
};
if( $(element).hasClass( "date" )) {
jsonObject.push({
date: $(element).text()
});
}
//links.push($(element))
});
console.log(jsonObject)
Result is:
{
title: 'TestA'
},
{ date: '10.10.10' },
{
title: 'TestB'
},
{ date: '10.10.11' }
I'd like it to be at this stage something like:
{
title: 'TestA'
,
date: '10.10.10' },
{
title: 'TestB'
,
date: '10.10.11' }
UPD:
Here's the example of HTML file:
<h1 class="header">H1_Header</h1>
<h2 class="date">Date</h2>
<p>A.</p>
<p>B.</p>
<p>С.</p>
<p>D.</p>
<a class="source">http://</a>
<h1 class="header">H1_Header2</h1>
<h2 class="date">Date2</h2>
<p>A2.</p>
<p>B2.</p>
<p>С2.</p>
<p>D2.</p>
<a class="source">http://2</a>
Thank you for your time!
Based on your example Html, it appears everything you are trying to collect is in a linear order, so you get a title, date, body and link then a new header with the associated items you want to collect, since this appears to not have the complication of having things being ordered in a non-linear fasion, you could do something like the following:
let jsonObject = null;
let newObject = false;
let appendParagraph = false;
let jObjects = [];
$('*').each((index, element) => {
if ($(element).hasClass("header")) {
//If newObject is true, push object into array
if(newObject)
jObjects.push(jsonObject);
//Reset the json object variable to an empty object
jsonObject = {};
//Reset the paragraph append boolean
appendParagraph = false;
//Set the header property
jsonObject.header = $(element).text();
//Set the boolean so on the next encounter of header tag the jsobObject is pushed into the array
newObject = true;
};
if( $(element).hasClass( "date" )) {
jsonObject.date = $(element).text();
}
if( $(element).prop("tagName") === "P") {
//If you are storing paragraph as one string value
//Otherwise switch the body var to an array and push instead of append
if(!appendParagraph){ //Use boolean to know if this is the first p element of object
jsonObject.body = $(element).text();
appendParagraph = true; //Set boolean to true to append on next p and subsequent p elements
} else {
jsonObject.body += (", " + $(element).text()); //append to the body
}
}
//Add the href property
if( $(element).hasClass("source")) {
//edit to do what you wanted here, based on your comment:
jsonObject.link = $(element).next().html();
//jsonObject.href= $(element).attr('href');
}
});
//Push final object into array
jObjects.push(jsonObject);
console.log(jObjects);
Here is a jsfiddle for this: https://jsfiddle.net/Lyojx85e/
I can't get the text of the anchor tags on the fiddle (I believe because nested anchor tags are not valid and will be parsed as seperate anchor tags by the browser), but the code provided should work in a real world example. If .text() doesn't work you can switch it to .html() on the link, I was confused on what you are trying to get on this one, so I updated the answer to get the href attribute of the link as it appears that is what you want. The thing is that the anchor with the class doesn't have an href attribute, so I'll leave it to you to fix that part for yourself, but this answer should give you what you need.
$('*').each((index, element) => {
var obj = {};
if ( $(element).hasClass( "header" ) ) {
obj.title = $(element).text();
};
if( $(element).hasClass( "date" )) {
obj.date = $(element).text()
}
jsonObject.push(obj);
});
I don't know about jQuery, but with JavaScript you can do with something like this.
const arr = [];
document.querySelectorAll("li").forEach((elem) => {
const obj = {};
const title = elem.querySelector("h2");
const date = elem.querySelector("date");
if (title) obj["title"] = title.textContent;
if (date) obj["date"] = date.textContent;
arr.push(obj);
});
console.log(arr);
<ul>
<li>
<h2>A</h2>
<date>1</date>
</li>
<li>
<h2>B</h2>
</li>
<li>
<date>3</date>
</li>
</ul>
Always use map for things like this. This should look something like:
let objects = $('.header').get().map(el => {
return {
date: $(el).attr('date'),
title: $(el).attr('title'),
}
})
I have this code:
function Userlist(users) {
var id = "#userlist";
$(id).empty();
users.forEach(function(value, key, map) {
userlistHTML += '<li><div class="userlist_pawn'+ value.pawn +'"></div><divclass="userlist_name">' + value.name + '</div></li>';
}
});
$(id).html(userlistHTML);
}
This is a userlist from my chat app, the users have different pawns that coming with value.pawn, i made css with this classes like userlist_pawnADMIN (orange pawn image), userlist_pawnMODERATOR (white pawn image) , userlist_pawnMEMBER (blue pawn image) , ADMIN, MODERATOR and MEMBER are the values from value.pawn depending on the rank of the user, I would like to make this order that ADMIN be on the 1st place in the list, MODERATOR on the 2nd place respectively MEMBER on the 3rd place, at the moment they appear in the order of connection on chat.
HTML:
<ul id="userlist">
</ul>
Could you try this one?
let order = ['ADMIN', 'MODERATOR', 'MEMBER'];
order.forEach(function(orderItem) {
users.forEach(function(value, key, map) {
if (orderItem === value.pawn) {
userlistHTML += '<li><div class="userlist_pawn'+ value.pawn +'"></div><divclass="userlist_name">' + value.name + '</div></li>';
}
})
})
This is what I was referring to -
get the user list / sort it by role / iterate over to get the string / replace the innerHTML with the string.
Note that I have used an object literal to determine the relative ordering of the user roles - since the desired outcome is not directly alphabbetical. This is used to determine the sort order of the users by referencing the value of the related user role (rather than simply alhabetically ordering the array).
Also not the one liner used for the sort - this could be split out into a seperate named function it it was needed by anothter function (and the sor order ould also be modified so that it shows differently for different users.
I also used the classes on the li's - and used that to give spacing between the groupings.
const users = [
{name: 'Arthur' , role: "Member"},
{name: 'Susan' , role: "Moderator"},
{name: 'Bob' , role: "Admin"},
{name: 'Sylvia' , role: "Member"},
{name: 'Gavin' , role: "Moderator"},
{name: 'Stuart' , role: "Admin"}
]
const roleOrder = {
Admin: 1,
Moderator: 2,
Member: 3
};
function Userlist(users) {
const id = "#userlist";
let userlistHTML ='';
const sortedUsers = users.sort(function(a, b){ return roleOrder[a.role] == roleOrder[b.role] ? 0 : +(roleOrder[a.role] > roleOrder[b.role]) || -1;});
sortedUsers.forEach(function(user) {
userlistHTML += '<li class="userlist_pawn'+ user.role +'"><span class="userlist_name">' + user.name + ' (' + user.role + ')</span></li>';
});
$(id).html(userlistHTML);
}
Userlist(users);
// gives the list in the order of admin > moderator > member
// Bob (admin)
// Stuart (admin)
// Susan (moderator)
// Gavin (moderator)
// Arthur (member)
// Sylvia (member)
ul {
list-style: none;
padding-left: 0;
}
.userlist_pawnAdmin {
color: red;
}
.userlist_pawnModerator {
color: blue;
}
.userlist_pawnMember {
color: green;
}
.userlist_pawnAdmin + .userlist_pawnModerator,
.userlist_pawnModerator + .userlist_pawnMember {
margin-top: 16px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id = "userlist"></ul>
I have been struggling with this for days, I have a function that takes in an object and loops through it creating rows with display columns in it (bootstrap), so I am using jQuery in order to select an id with a value from the object itself and appending to it, but for some reason the id selector that contains the variable is not seen and does not append to the div with the id?
function add_to_page(product) {
//this is the main row that will contain the columns
var rows = $('<div class="row" ></div>');
for (var category in products) {
for (var i = 0; i < products[category].length; i++) {
rows.append($("<div id='" +products[category][i].id +"'></div>").html($('<img>').attr('src', products[category][i].img)));
//the below code is never executed it only creates the image and stops appending after that
$('#' + products[category][i].id).append($('<div class=name>').text(products[category][i].name));
$('#' + products[category][i].id).append($('<div class=category>').text(products[category][i].category));
$('#' + products[category][i].id).append($('<div class=price>').text('Price: ' + products[category][i].price + 'L.E'));
$('#' + products[category][i].id).addClass('col-md-3');
//the below code is to create a new row after filling each row with 4 display columns
if ( i % 3 == 0 && i != 0 ) {
$('#content').append(rows.clone(true));
rows = $('<div class="row"></div>');
}
}
}
}
here is the html i am trying to append to:
<div class="container full-width">
<div id="content"></div>
</div>
i am calling the function normally , no error in the console
add_to_page(products);
You cannot use a selector to find an element you have not yet added to the document (unless you use the second argument of $(selector, context)). But for your purposes, you can just use the fact that the append method can accept more than one argument.
With some other changes to make your code more jQuery-like, you get this:
function add_to_page(products) {
//this is the main row that will contain the columns
var rows = $('<div>').addClass("row");
for (var category in products) {
for (var item of products[category]) {
rows.append(
$("<div>").attr("id", item.id).addClass('col-md-3').append(
$('<img>').attr('src', item.img),
$('<div>').addClass('name').text(item.name),
$('<div>').addClass('category').text(item.category),
$('<div>').addClass('price').text('Price: ' + item.price + 'L.E')
)
);
//the below code is to create a new row after filling each row with 3 display columns
if ( rows.children().length >= 3 ) {
$('#content').append(rows);
rows = $('<div>').addClass("row");
}
}
}
// Flush any rows that were not yet added:
if ( rows.children().length ) {
$('#content').append(rows);
}
}
// Sample data
var products = {
fruit: [{
name: 'apple',
price: 2.20,
img: "https://www.colourbox.com/preview/7011130-apples-on-table-and-knife.jpg",
category: 'fruit'
}, {
name: 'kiwi',
price: 3.10,
img: "https://www.colourbox.com/preview/10157893-kiwi-fruit.jpg",
category: 'fruit'
}, {
name: 'banana',
price: 1.50,
img: "https://www.colourbox.com/preview/6294218-banana.jpg",
category: 'fruit'
}],
vegetables: [{
name: 'lettuce',
price: 0.90,
img: "https://www.colourbox.com/preview/2347661-fresh-salad-leaves-assortment-in-a-basket.jpg",
category: 'vegetables'
}, {
name: 'radish',
price: 1.60,
img: "https://www.colourbox.com/preview/3602479-red-radish.jpg",
category: 'vegetables'
}]
};
add_to_page(products);
.col-md-3 { display: inline-block; margin: 5px }
img { max-width: 100px }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container full-width">
<div id="content"></div>
</div>
Append all your dynamic html to an element stored in a variable then append that variable to the DOM. that selector wont work bc the element you are trying to access doesnt exists yet.
Do something like:
var el = $('<div' + id + '></div>')
el.append(your row constructed html)
Then append that to your rows at the end of the 2nd for loop.
EDIT
for(.... row iteration code) {
var el = $('<div' + id + '></div>');
el.append('your image goes here');
el.append('name element');
...
rows.append(el);
}
Use #trincot's superior answer, but note that you can also pass an entire HTML string into jQuery's .html(). E.G:
function add_to_page(products) {
var $content = $("#content"),
cols=3, //number of columns
$row,
i=0;
for (var category in products) {
for (var item of products[category]) {
if(i%cols == 0){//add a new row
$row = $("<div class='row'></div>");
$content.append($row);
}
//add the item to the row
$row.append($("<div class='col-md-"+cols+"' id='" +item.id +"'><img src='"+item.img+"' /><div class=name>"+item.name+"</div><div class=category>"+item.category+"</div><div class=price>Price: "+item.price+"L.E</div>"));
i++;
}
}
}
var products = {
"jam" : [
{"id":1,"name":"strawberry", "category":"jam", "price":123, "img":"pic.gif"},
{"id":2,"name":"rasberry", "category":"jam", "price":456, "img":"pic.gif"},
{"id":3,"name":"madberry", "category":"jam", "price":123, "img":"pic.gif"},
{"id":4,"name":"sadberry", "category":"jam", "price":456, "img":"pic.gif"}
],
"bees" : [
{"id":4,"name":"baz", "category":"bee", "price":1, "img":"pic.gif"},
{"id":5,"name":"buzz", "category":"bee", "price":2, "img":"pic.gif"}
]
};
//console.log(products);
add_to_page(products);
.row {display:block; border:1px solid blue;}
.row div[id] {display:inline-block; border:1px solid red; width:100px; margin:5px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container full-width">
<div id="content"></div>
</div>
You need to restructure the code.. are you able to find the element first
rows.append($("")
Instead use products.category [i].id then use jQuery wrap () to wrap the columns to row .
Suggest me any good mustache doc. Also i want to know in a mushtach loop how do i get the count or the loop no. I mean how can i do a for loop in mustache.
In the below code i wish to change the id in every loop
<script src="http://github.com/janl/mustache.js/raw/master/mustache.js"></script>
<script>
var data, template, html;
data = {
name : "Some Tuts+ Sites",
big: ["Nettuts+", "Psdtuts+", "Mobiletuts+"],
url : function () {
return function (text, render) {
text = render(text);
var url = text.trim().toLowerCase().split('tuts+')[0] + '.tutsplus.com';
return '' + text + '';
}
}
};
template = '<h1> {{name}} </h1><ul> {{#big}}<li id="no"> {{#url}} {{.}} {{/url}} </li> {{/big}} </ul>';
html = Mustache.to_html(template, data);
document.write(html)
</script>
<body></body>
You can't get at the array index in Mustache, Mustache is deliberately simple and wants you to do all the work when you set up your data.
However, you can tweak your data to include the indices:
data = {
//...
big: [
{ i: 0, v: "Nettuts+" },
{ i: 1, v: "Psdtuts+" },
{ i: 2, v: "Mobiletuts+" }
],
//...
};
and then adjust your template to use {{i}} in the id attributes and {{v}} instead of {{.}} for the text:
template = '<h1> {{name}} </h1><ul> {{#big}}<li id="no-{{i}}"> {{#url}} {{v}} {{/url}} </li> {{/big}} </ul>';
And as an aside, you probably want to include a scheme in your url:
url : function () {
return function (text, render) {
text = render(text);
var url = text.trim().toLowerCase().split('tuts+')[0] + '.tutsplus.com';
return '' + text + '';
//---------------^^^^^^^
}
}
Demo: http://jsfiddle.net/ambiguous/SFXGG/
Expanding on #mu's answer, you could also keep an index in the data object and have the template refer to it and the function increment it. So you wouldn't need to add i to each item.
see demo : http://jsfiddle.net/5vsZ2/