I want to build a DOM tree using a JS object (classic topic). I achieved most of the work but I am getting a non sense result: DOM tree appear to be created correctly BUT all nodes are rendered flatten on a single line and Input element is clipped.
I highly suspect the buildNode function not working correctly, but I can not find the flaw.
/**** FILTERS ****/
// TODO : change crappy filter using dictionnary
const attrOnly = (str) => !(str === 'content' || str === 'tag' || str === 'children');
/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
hasContent = (obj) => obj.hasOwnProperty('content');
// TODO: search for namespace given a tag name (SVG)
const findNameSpace = (str) => null;
// Build node with correct attributes and values
const buildNode = function (element, parent) {
const tag = (element.tag || 'div'),
tagAttr = Object.keys(element).filter(attrOnly),
node = document.createElementNS(findNameSpace(tag), tag);
tagAttr.forEach(
(attr) => node.setAttributeNS(findNameSpace(tag), attr, element[attr])
);
hasContent(element) ? node.innerHTML = element.content : null;
return parent.appendChild(node);
}
// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
const current = f(element, parent);
// Reccursively walk children, if necessary
(hasChildren(element) && element.children.forEach(
child => walkTree(f, child, current)
));
};
let tree = {
tag: 'div',
id: 'tree',
children: [{
tag: 'section',
id: 'section-l1-1',
class: 'l1',
content: 'Use <em>me</em> as I am, I am gorgeous!',
children: [{
tag: 'div',
id: 'div-l2',
class: 'l2',
children: [{
tag: 'p',
content: 'Here is a nested paragraph.'
}]
}, {
tag: 'form',
id: 'form-l2',
class: 'l2',
onsubmit: 'alert("Function called!");',
children: [{
tag: 'input',
type: 'text',
id: 'input-l3',
class: 'l3',
value: 'self-closing tag case!'
}]
}]
}, {
tag: 'footer',
id: 'end-page',
class: 'l1',
content: 'This is a running experiment.'
}]
};
walkTree(buildNode, tree, document.getElementById('seed'));
#seed div,form,input {
display: block;
}
<div id="seed"></div>
So I found that namespaces handling was my problem. As I wanted to get render HTML elements and SVG elements at the same time, I needed that...
I did not find a proper fix to that problem so I set lower expectations and only render HTML using document.createElement() (and Node.setAttribute()) instead of document.createElementNS() (and Node.setAttributeNS()).
To see how far I get with namespace handling:
/* DICTIONARIES */
const nonAttr = ['content', 'tag', 'children'],
// Search for tag's special namespace. Source: https://www.w3.org/TR/2011/WD-html5-20110525/namespaces.html
tagNamespace = {
'svg': 'https://www.w3.org/2000/svg'
};
/**** FILTERS ****/
const attrOnly = (str) => !(nonAttr.includes(str));
/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
hasContent = (obj) => obj.hasOwnProperty('content');
// Search for namespace given a tag name
const findNameSpace = (str) => (tagNamespace[str] || 'http://www.w3.org/1999/xhtml');
// Build node with correct attributes and values
const buildNode = function (element, parent) {
const tag = (element.tag || 'div'),
tagAttr = Object.keys(element).filter(attrOnly),
node = document.createElementNS(findNameSpace(tag), tag);
tagAttr.forEach(
(attr) => node.setAttribute(attr, element[attr])
);
hasContent(element) ? node.innerHTML = element.content : null;
return parent.appendChild(node);
}
// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
const current = f(element, parent);
// Reccursively walk children, if necessary
(hasChildren(element) && element.children.forEach(
child => walkTree(f, child, current)
));
};
let tree = {
tag: 'div',
id: 'tree',
children: [{
tag: 'section',
id: 'section-l1-1',
class: 'l1',
content: 'Use <em>me</em> as I am, I am gorgeous!',
children: [{
tag: 'div',
id: 'div-l2',
class: 'l2',
children: [{
tag: 'p',
content: 'Here is a nested paragraph.'
}]
}, {
tag: 'form',
id: 'form-l2',
class: 'l2',
onsubmit: 'alert("Function called!");',
action: '',
method: 'GET',
children: [{
tag: 'input',
type: 'text',
id: 'text-l3',
name: 'text-l3',
class: 'l3',
placeholder: 'self-closing tag case!'
},{
tag: 'input',
type: 'submit',
id: 'submit-l3',
name: 'submit-l3',
class: 'l3',
value: 'submit!'
}]
}]
}, {
tag: 'footer',
id: 'end-page',
class: 'l1',
content: 'This is a running experiment.',
children: [{
tag: 'svg',
class: 'l2',
children : [{
tag: 'rect',
width: '10',
height: '10',
fill: 'black'
}]
}]
}]
};
walkTree(buildNode, tree, document.getElementById('seed'));
.l1 {
background-color: DarkGrey;
}
.l2 {
background-color: Grey;
}
.l3 {
background-color: LightGrey;
}
.l4 {
background-color: White;
}
<div id="seed"></div>
To see working code without namespace support:
/* DICTIONARIES */
const nonAttr = ['content', 'tag', 'children'];
/**** FILTERS ****/
const attrOnly = (str) => !(nonAttr.includes(str));
/**** TESTS ****/
const hasChildren = (obj) => obj.hasOwnProperty('children'),
hasContent = (obj) => obj.hasOwnProperty('content');
// Build node with correct attributes and values
const buildNode = function(element, parent) {
const tag = (element.tag || 'div'),
tagAttr = Object.keys(element).filter(attrOnly),
node = document.createElement(tag);
tagAttr.forEach(
(attr) => node.setAttribute(attr, element[attr])
);
hasContent(element) ? node.innerHTML = element.content : null;
return parent.appendChild(node);
}
// Walk along the tree mapping current element with function f.
function walkTree(f, element, parent) {
const current = f(element, parent);
// Reccursively walk children, if necessary
(hasChildren(element) && element.children.forEach(
child => walkTree(f, child, current)
));
};
let tree = {
tag: 'div',
id: 'tree',
children: [{
tag: 'section',
id: 'section-l1-1',
class: 'l1',
content: 'Use <em>me</em> as I am, I am gorgeous!',
children: [{
tag: 'div',
id: 'div-l2',
class: 'l2',
children: [{
tag: 'p',
content: 'Here is a nested paragraph.'
}]
}, {
tag: 'form',
id: 'form-l2',
class: 'l2',
onsubmit: 'alert("Function called!");',
action: '',
method: 'GET',
children: [{
tag: 'input',
type: 'text',
id: 'text-l3',
name: 'text-l3',
class: 'l3',
placeholder: 'self-closing tag case!'
}, {
tag: 'input',
type: 'submit',
id: 'submit-l3',
name: 'submit-l3',
class: 'l3',
value: 'submit!'
}]
}]
}, {
tag: 'footer',
id: 'end-page',
class: 'l1',
content: 'This is a running experiment.',
// SVG CAN'T GET RENDERED BECAUSE NAMESPACES
children: [{
tag: 'svg',
class: 'l2',
children: [{
tag: 'rect',
fill: 'black'
}]
}]
}]
};
walkTree(buildNode, tree, document.getElementById('seed'));
.l1 {
background-color: DarkGrey;
}
.l2 {
background-color: Grey;
}
.l3 {
background-color: LightGrey;
}
.l4 {
background-color: White;
}
<div id="seed"></div>
PS. I have to say that I have dirt in my mouth...
Related
I have this object:
popup_data = {
club: {
type: 'club',
type_img: {
header: 'CLUB HEADER',
img: 'https://source.unsplash.com/random/800x600',
sub_header: 'CLUB SUB HEADER',
content: 'TEXT',
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{ id: () => this.club.type + '-' + 'type_img',
position: () => this.club.type_img.hotspot_position,
},
]
},
How to get type and type_img.hotspot_position from these nested functions
just use the var name, popup_data
popup_data = {
club: {
type: 'club',
type_img: {
header: 'CLUB HEADER',
img: 'https://source.unsplash.com/random/800x600',
sub_header: 'CLUB SUB HEADER',
content: 'TEXT',
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{ id: () => popup_data.club.type + '-' + 'type_img',
position: () => popup_data.club.type_img.hotspot_position,
},
]
},
Read This and This
What is this?
In JavaScript, the this keyword refers to an object.
Which object depends on how this is being invoked (used or called).
The this keyword refers to different objects depending on how it is used:
In an object method, this refers to the object.
Alone, this refers to the global object.
In a function, this refers to the global object.
In a function, in strict mode, this is undefined.
In an event, this refers to the element that received the event.
Methods like call(), apply(), and bind() can refer this to any object.
You can access it by the variable name that you assign the entire object into.
popup_data = {
club: {
type: 'club',
type_img: {
header: 'CLUB HEADER',
img: 'https://source.unsplash.com/random/800x600',
sub_header: 'CLUB SUB HEADER',
content: 'TEXT',
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{ id: () => popup_data.club.type + '-' + 'type_img',
position: () => popup_data.club.type_img.hotspot_position,
},
]
},
Since you are using ES6 arrow function, this will not work at all. And if you used normal function, this would be the object inside hotspots_array, not the entire object.
If you have more than one item, and want all those items to have access to the root, you can bind the function. You will need to use none arrow functions here, as they don't have a this..
You can even extract your id and position function out of the object too, so that your not creating multiple instances of the exact same function.
eg..
function id() {
return this.club.type + '-' + 'type_img';
}
function position() {
return this.club.type_img.hotspot_position;
}
const popup_data = {
club: {
type: 'club',
type_img: {
header: 'CLUB HEADER',
img: 'https://source.unsplash.com/random/800x600',
sub_header: 'CLUB SUB HEADER',
content: 'TEXT',
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{
name: 'test',
id, position
}, {
name: 'test2',
id, position
}
]
}
};
const popup_data_2 = {
club: {
type: 'this is club 2',
type_img: {
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{
id, position
}
]
}
};
function bindFunctions(r, a) {
Object.entries(a).map(([k, v]) => {
const tp = typeof v;
if (tp === 'function') {
a[k] = v.bind(r);
} else if (tp === 'object') {
bindFunctions(r, v);
}
});
}
bindFunctions(popup_data, popup_data);
bindFunctions(popup_data_2, popup_data_2);
console.log(popup_data.club.hotspots_array[0].id());
console.log(popup_data.club.hotspots_array[0].position());
console.log(popup_data_2.club.hotspots_array[0].id());
Another option, having a single root item can be restrictive. If you wanted something more fancy I would create your Object using a function and use the power of closures. The example below I've added another prop that then has access to it's array.
function makeObject(data) {
const root = data;
for (const h of data.club.hotspots_array) {
//now lets add our functions
h.id = () => root.club.type + '-' + 'type_img';
h.position = () => root.club.type_img.hotspot_position;
h.array = data.club.hotspots_array;
}
return root;
}
const o1 = makeObject({
club: {
type: 'club',
type_img: {
header: 'CLUB HEADER',
img: 'https://source.unsplash.com/random/800x600',
sub_header: 'CLUB SUB HEADER',
content: 'TEXT',
hotspot_position: [5, -1.5, 2.5]
},
hotspots_array: [
{
name: 'test',
}, {
name: 'test2',
}
]
}
});
console.log(o1.club.hotspots_array[0].id());
console.log(o1.club.hotspots_array[0].position());
console.log('array:' + o1.club.hotspots_array[0].array.length);
I have this array of JSON objects:
and I want to add a unique ID (string) to each entry, like this:
let myTree = [
{
text: 'Batteries',
id: '0',
children: [
{
text: 'BatteryCharge',
id: '0-0'
},
{
text: 'LiIonBattery',
id: '0-1'
}
]
},
{
text: 'Supplemental',
id: '1',
children: [
{
text: 'LidarSensor',
id: '1-0',
children: [
{
text: 'Side',
id: '1-0-0'
},
{
text: 'Tower',
id: '1-0-1'
}
]
}
]
}
]
I just can't think of the right logic to achieve this. I have written this recursive function, which obviously does not achieve what I want:
function addUniqueID(tree, id=0) {
if(typeof(tree) == "object"){
// if the object is not an array
if(tree.length == undefined){
tree['id'] = String(id);
}
for(let key in tree) {
addUniqueID(tree[key], id++);
}
}
}
addUniqueID(myTree);
How can I solve this problem?
Instead of using a number/id in the recursive function I build a string.
let myTree = [{
text: 'Batteries',
children: [{
text: 'BatteryCharge'
},
{
text: 'LiIonBattery'
}
]
},
{
text: 'Supplemental',
children: [{
text: 'LidarSensor',
children: [{
text: 'Side'
},
{
text: 'Tower'
}
]
}]
}
];
function addUniqueID(arr, idstr = '') {
arr.forEach((obj, i) => {
obj.id = `${idstr}${i}`;
if (obj.children) {
addUniqueID(obj.children, `${obj.id}-`);
}
});
}
addUniqueID(myTree);
console.log(myTree);
I hope you are well.
why don't you consider using uuid?
In node there is the uuid module which you can use to generate unique identifiers, I share a base example:
install:
npm install uuid
npm i --save-dev #types/uuid
code:
import {v4 as uuid} from 'uuid';
let _id = uuid();
I have an array of objects that have deeply nested children and sometimes children within children. I am attempting to handle this recursively, but I am getting stuck.
The goal of the function is to return a single data object that matches the id.
My Data looks like this:
data: [
{
id: 'RAKUFNUBNY00UBZ40950',
name: 'Grade 1 Cover',
activityId: 'RAKUFNUBNY00UBZ40950',
nodeType: 'activity',
suppressed: false,
hidden: false
},
{
children: [
{
id: 'SLWDYEQHTZAFA3ALH195',
name: 'Build Background Video',
activityId: 'SLWDYEQHTZAFA3ALH195',
nodeType: 'activity',
suppressed: false,
hidden: false,
assetReference: {
referenceId: 'UWFHA5A1E0EGKCM0W899',
assetType: 'image'
}
},
{
children: [
{
id: 'HQUCD2SSRKMYC2PJM636',
name: 'Eat or Be Eaten Splash Card',
activityId: 'HQUCD2SSRKMYC2PJM636',
nodeType: 'activity',
suppressed: false,
hidden: true
},
{
children: [
{
id: 'ZDTWEZFL13L8516VY480',
name: 'Interactive Work Text: Eat or Be Eaten',
activityId: 'ZDTWEZFL13L8516VY480',
nodeType: 'activity',
suppressed: false,
hidden: true,
defaultLaunchMode: 'modal'
}
],
My attempt at solving this is like this:
findNode(id, currentNode) {
console.log('id', id);
console.log('findNode', currentNode);
var i, currentChild, result, counter;
counter = 0;
console.log('first conditional statement', currentNode);
if (id && currentNode.id === id) {
return currentNode[0];
} else {
counter++;
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
console.log('counter', counter);
console.log('currentNode', currentNode[counter]);
console.log('currentNode Children', currentNode.children);
for (i = counter; i < currentNode.children.length; i += 1) {
console.log(currentNode[i].children[i]);
currentChild = currentNode[i].children[i];
// Search in the current child
result = this.findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}
The code above fails because I having an extremely difficult time keeping track of a counter to loop through everything.
I also added a sample picture of my data output to give you a better example of how my data is structured. Any help will be greatly appreciated.
You shouldn't need a counter to locate a single node with a matching id. Try this simpler approach:
function findNode (id, array) {
for (const node of array) {
if (node.id === id) return node;
if (node.children) {
const child = findNode(id, node.children);
if (child) return child;
}
}
}
It will return undefined if there is no match.
To avoid the need for manual iteration, you might consider using an array method like reduce instead - return the accumulator if it's truthy (that is, an object was found already), or return the object being iterated over if the ID matches, or recursively iterate over the object's children to find a match.
const data=[{id:'RAKUFNUBNY00UBZ40950',name:'Grade 1 Cover',activityId:'RAKUFNUBNY00UBZ40950',nodeType:'activity',suppressed:!1,hidden:!1},{children:[{id:'SLWDYEQHTZAFA3ALH195',name:'Build Background Video',activityId:'SLWDYEQHTZAFA3ALH195',nodeType:'activity',suppressed:!1,hidden:!1,assetReference:{referenceId:'UWFHA5A1E0EGKCM0W899',assetType:'image'}},{children:[{id:'HQUCD2SSRKMYC2PJM636',name:'Eat or Be Eaten Splash Card',activityId:'HQUCD2SSRKMYC2PJM636',nodeType:'activity',suppressed:!1,hidden:!0},{children:[{id:'ZDTWEZFL13L8516VY480',name:'Interactive Work Text: Eat or Be Eaten',activityId:'ZDTWEZFL13L8516VY480',nodeType:'activity',suppressed:!1,hidden:!0,defaultLaunchMode:'modal'}],}],}],}]
function findId(id, arr) {
return arr.reduce((a, item) => {
if (a) return a;
if (item.id === id) return item;
if (item.children) return findId(id, item.children);
}, null);
}
console.log(findId('HQUCD2SSRKMYC2PJM636', data));
If your ids are unique and finding an object by id is a common task, you might want to consider creating a lookup object to improve performance. Creating the lookup object is an O(n) task; afterwards, looking up an object by id is O(1).
const data = [ { id: 'RAKUFNUBNY00UBZ40950', name: 'Grade 1 Cover', activityId: 'RAKUFNUBNY00UBZ40950', nodeType: 'activity', suppressed: false, hidden: false }, { children: [ { id: 'SLWDYEQHTZAFA3ALH195', name: 'Build Background Video', activityId: 'SLWDYEQHTZAFA3ALH195', nodeType: 'activity', suppressed: false, hidden: false, assetReference: { referenceId: 'UWFHA5A1E0EGKCM0W899', assetType: 'image' } }, { children: [ { id: 'HQUCD2SSRKMYC2PJM636', name: 'Eat or Be Eaten Splash Card', activityId: 'HQUCD2SSRKMYC2PJM636', nodeType: 'activity', suppressed: false, hidden: true }, { children: [ { id: 'ZDTWEZFL13L8516VY480', name: 'Interactive Work Text: Eat or Be Eaten', activityId: 'ZDTWEZFL13L8516VY480', nodeType: 'activity', suppressed: false, hidden: true, defaultLaunchMode: 'modal' } ] } ] } ] } ];
const lookup = {};
const registerIds = a => {
a.forEach(o => {
if ('id' in o) {
lookup[o.id] = o;
} else if ('children' in o) {
registerIds(o.children)
}
});
}
registerIds(data);
console.log(lookup)
Sorry for my two cents, just want to add a universal method that includes nested arrays
const cars = [{
id: 1,
name: 'toyota',
subs: [{
id: 43,
name: 'supra'
}, {
id: 44,
name: 'prius'
}]
}, {
id: 2,
name: 'Jeep',
subs: [{
id: 30,
name: 'wranger'
}, {
id: 31,
name: 'sahara'
}]
}]
function searchObjectArray(arr, key, value) {
let result = [];
arr.forEach((obj) => {
if (obj[key] === value) {
result.push(obj);
} else if (obj.subs) {
result = result.concat(searchObjectArray(obj.subs, key, value));
}
});
console.log(result)
return result;
}
searchObjectArray(cars, 'id', '31')
searchObjectArray(cars, 'name', 'Jeep')
I hope this helps someone
I have a very simple file that should scrape a single webpage for some data. After reading around I set myself to artoo, request and cheerio but now I'm stuck. This is the code I have so far.
request('http://www.ciclopi.eu/frmLeStazioni.aspx?ID=144', function (error, response, html) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(html);
artoo.bootstrap(cheerio);
var scraper = $('span.Stazione, span.TableComune, span.Red').scrape({
class: 'class',
content: 'text'
});
console.log(scraper);
}
});
This code resolves into the scraper variable as a json object structured like this:
[ { class: 'Stazione', content: 'Galleria Gerace' },
{ class: 'TableComune', content: 'Via Carlo Matteucci' },
{ class: 'Red', content: '7 bici libere5 posti disponibili' },
{ class: 'Stazione', content: 'C. Marchesi' },
{ class: 'TableComune', content: 'Via M. Valgimigli' },
{ class: 'Red', content: '2 bici libere10 posti disponibili' },
{ class: 'Stazione', content: 'CNR-Praticelli' },
{ class: 'TableComune', content: 'Via G. Moruzzi' },
{ class: 'Red', content: '7 bici libere7 posti disponibili' } ]
What I need is to rearrange the scraper object as something like this
scraper = [
{
"Name": the content for class Stazione,
"Address": the content for class TableComune,
"Bikes": the content for class Red
},
{
"Name": the content for class Stazione,
"Address": the content for class TableComune,
"Bikes": the content for class Red
}
...
]
I'm really in the dark here, hopefully I explained myself..
For this kind of mutations I would use Array.prototype.reduce method.
const data = [
{ class: 'Stazione', content: 'Galleria Gerace' },
{ class: 'TableComune', content: 'Via Carlo Matteucci' },
{ class: 'Red', content: '7 bici libere5 posti disponibili' },
{ class: 'Stazione', content: 'C. Marchesi' },
{ class: 'TableComune', content: 'Via M. Valgimigli' },
{ class: 'Red', content: '2 bici libere10 posti disponibili' },
{ class: 'Stazione', content: 'CNR-Praticelli' },
{ class: 'TableComune', content: 'Via G. Moruzzi' },
{ class: 'Red', content: '7 bici libere7 posti disponibili' }
];
const result = data.reduce((acc, item) => {
if (item.class === 'Stazione') {
acc.push({ 'Name': item.content });
} else {
const last = acc[acc.length - 1];
if (item.class === 'TableComune') {
last['Address'] = item.content;
} else { // if (item.class === 'Red')
last['Bikes'] = item.content;
}
}
return acc;
}, []);
Here we are iterating through the initial data array and generating the result array by 2 conditional points:
if current item is Stazione then we are pushing a new object to the result array; this object will have Name property corresponded to current Stazione content
otherwise we are updating the last element of the result array: TableComune content will move to Address property, Red content will move to Bikes property.
The procedure will work only in case of strict order of Stazione-TableComune-Red objects in the initial data array.
I'm new to Handlebars and while I found a workaround, I'm wondering why one registered helper works and one doesn't. The example that doesn't work is an example from the HB docs.
HTML:
<ul class="global-nav clearfix">
{{#each data}}
<li>{{text}}</li>
{{/each}}
</ul>
...
<ul class="content-nav clearfix">
{{#each data}}
<li>{{text}}</li>
{{/each}}
</ul>
Data:
var nav = [
{
name: 'global',
selector: $('.global-nav'),
data: [
{
text: 'Page 1',
href: 'page1.html'
}, {
text: 'Page 2',
href: 'page2.html'
}
],
name: 'content',
selector: $('.content-nav'),
data: [
{
text: 'Section 1',
href: '#section1'
}, {
text: 'Section 2',
href: '#section2'
}
]
}
];
Compiler:
$.each(nav, function() {
var obj = this,
src = obj.selector.html(),
template = Handlebars.compile(src),
html = template(obj.data);
obj.selector.html(html);
});
HB Helper (does not work - context is undefined):
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
HB Helper (does work using this instead of context):
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=this.length; i<j; i++) {
ret = ret + options.fn(this[i]);
}
return ret;
});
Any helps is appreciated.
Before looking at the rest of the JS, can I point out that the JSON looks wrong.
Name, selector and data are overwritten the second time they occur in your JSON. If this is just because of some bits being omitted when pasting to SO, then do ignore me ;o)
But if that is the real JSON, then it needs changing before looking at any functional stuff
<script>
var nav = [
{
name: 'global',
selector: $('.global-nav'),
data: [
{
text: 'Page 1',
href: 'page1.html'
}, {
text: 'Page 2',
href: 'page2.html'
}
]
}, // this line
{ // and this line added
name: 'content',
selector: $('.content-nav'),
data: [
{
text: 'Section 1',
href: '#section1'
}, {
text: 'Section 2',
href: '#section2'
}
]
}
];
</script>