I wanted to translate texts in my web site I used this code, I know that I'm close to get the right solution.
Somewhere in my HTML code:
<ul class="nav navbar-nav navbar-right">
<li>A PREPOS</li>
<li>SERVICES</li>
<li>PORTFOLIO</li>
<li>PRICING</li>
<li>CONTACT</li>
<li> </li>
<li ><a class="lang">FR</a></li>
<li ><a class="lang">EN</a></li>
</ul>
and this is my JavaScript code :
var dictionary, set_lang;
// Object literal behaving as multi-dictionary
dictionary = {
"fr": {
"menu" : {
"about": "à propos",
"services": "services"
}
},
"en": {
"menu" : {
"about": "about",
"services": "services"
}
}
};
// Function for swapping dictionaries
set_lang = function (dictionary) {
$("[data-translate]").text(function () {
var key = $(this).data("translate");
if (dictionary.hasOwnProperty(key)) {
console.log(dictionary[key]);
return dictionary[key];
}
});
};
// Swap languages when menu changes
$(".lang").click(function() {
var language = $(this).html().toLowerCase();
console.log(language);
if (dictionary.hasOwnProperty(language)) {
set_lang(dictionary[language]);
}
});
// Set initial language to French
set_lang(dictionary.fr);
});
In this part :
<li>A PREPOS</li>
The Text 'A PREPOS' can't be translated, but when I change It to:
<li>A PREPOS</li>
I can see my object 'menu' using console.log
Your dictionary logic is totally off. services i.e: will always be services and there's not good reason to use an additional menu layer. Redundant and repetitive should be avoided.
Rather use properties like:
var dictionary = { // props in alphabetical order ok? promise?
// prop : {fr, en}
about : {fr:"à propos", en:"about"},
services : {fr:"services", en:"servicesssss"}
};
function translate( lan ) {
$("[data-translate]").text(function(){
var data = this.dataset.translate.split("|");
var prop = data[0]; // the dictionary property name
var style = data[1]; // "uppercase", "lowercase", "capitalize"
if(!prop in dictionary) return console.error("No "+ prop +" in dictionary");
var trans = dictionary[prop][lan]; // The translated word
// Do we need to apply styles?
if(style==="capitalize"){
trans = trans.charAt(0).toUpperCase() + trans.slice(1);
} else if(style==="uppercase"){
trans = trans.toUpperCase();
} else if( style==="lowercase"){
trans = trans.toLowerCase();
}
return trans;
});
}
// Swap languages when menu changes
$("[data-lang]").click(function() {
translate( this.dataset.lang );
});
// Set initial language to French
translate("fr");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav navbar-nav navbar-right">
<li>A PREPOS</li>
<li>SERVICES</li>
<li> </li>
<li><a data-lang="fr">FR</a></li> <!-- data-* attributes can be helpful -->
<li><a data-lang="en">EN</a></li>
</ul>
Your problem is that your call to dictionary.hasOwnProperty("menu.about") returns false.
You need to traverse the tree of objects so you're getting the property like this:
dictionary["menu"]["about"]
I've put together a simple recursive example on how you can traverse the tree of objects.
// Object literal behaving as multi-dictionary
dictionary = {
"fr": {
"menu": {
"about": "à propos",
"services": "services"
}
},
"en": {
"menu": {
"about": "about",
"services": "services"
}
}
};
// Function for swapping dictionaries
set_lang = function(dictionary) {
$("[data-translate]").text(function() {
var key = $(this).data("translate");
return parseSubObject(dictionary, key);
});
};
function parseSubObject(obj, str) {
var props = str.split(".");
var thisProp = props.shift();
if (obj.hasOwnProperty(thisProp)) {
if (props.length == 0) {
return obj[thisProp];
} else {
return parseSubObject(obj[thisProp], props.join('.'));
}
} else {
return null;
}
}
// Swap languages when menu changes
$(".lang").click(function() {
var language = $(this).text().toLowerCase();
if (dictionary.hasOwnProperty(language)) {
set_lang(dictionary[language]);
}
});
// Set initial language to French
set_lang(dictionary.fr);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="nav navbar-nav navbar-right">
<li>A PREPOS</li>
<li>SERVICES</li>
<li>PORTFOLIO</li>
<li>PRICING</li>
<li>CONTACT</li>
<li> </li>
<li><a class="lang">FR</a></li>
<li><a class="lang">EN</a></li>
</ul>
You're assuming that dictionary.fr.menu.about (à propos) is the same as dictionary.fr['menu.about'] (undefined).
Note the data structure of each section of dictionary.fr.menu.about:
dictionary: Object
dictionary.fr: Object
dictionary.fr.menu: Object
dictionary.fr.menu.about: String
In order to get to the String, you need to walk the objects, which you can do with this code:
set_lang = function(dictionary) {
$("[data-translate]").text(function() {
var key = $(this).data("translate").split('.'),
val = dictionary[key.shift()];
while(key.length) {
val = val[key.shift()];
}
return val;
});
};
Snippet:
var dictionary, set_lang;
// Object literal behaving as multi-dictionary
dictionary = {
"fr": {
"menu": {
"about": "à propos",
"services": "services"
}
},
"en": {
"menu": {
"about": "about",
"services": "services"
}
}
};
// Function for swapping dictionaries
set_lang = function(dictionary) {
$("[data-translate]").text(function() {
var key = $(this).data("translate").split('.'),
val = dictionary[key.shift()];
while(key.length) {
val = val[key.shift()];
}
return val;
});
};
// Swap languages when menu changes
$(".lang").click(function() {
var language = $(this).html().toLowerCase();
console.log(language);
if (dictionary.hasOwnProperty(language)) {
set_lang(dictionary[language]);
}
});
// Set initial language to French
set_lang(dictionary.fr);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav navbar-nav navbar-right">
<li>A PREPOS</li>
<li>SERVICES</li>
<li>PORTFOLIO</li>
<li>PRICING</li>
<li>CONTACT</li>
<li> </li>
<li><a class="lang">FR</a></li>
<li><a class="lang">EN</a></li>
</ul>
Related
I tried creating a small video library where one div is split into two parts: 1) a menu on the left with the titles of the movies and 2) the rest of the div on the right being a video window that changes it's video source according to the selected title on the menu. I gave the li elements that housed the titles id's and used jQuery/JavaScript to retrieve the title and to assign a new video source based on that title, but it isn't working and I also can't claim to completely understand what I've done. The code is as follows:
HTML
<div class="miyazaki">
<div class="menu">
<ul>
<li><a id="Gulliver">Gulliver's Travels</a></li>
<li><a id="Howl">Howl's Moving Castle</a></li>
<li><a id="Kiki">Kiki's Delivery Service</a></li>
<li><a id="Castle">Castle of Cagoliostro</a></li>
<li><a id="Totoro">"My Neighbor Totoro</a></li>
<li><a id="Ponyo">Ponyo</a></li>
<li><a id="Mononoke">"Princess Mononoke</a></li>
<li><a id="Spirited">Spirited Away</a></li>
<li><a id="Sky">The Sky's Castle Laputa</a></li>
<li><a id="Nausicaa">Nausicaa Valley of the Wind</a></li>
<li><a id="Cat">"The Cat Returns</a></li>
</ul>
</div>
</div>
JavaScript
function Hayato() {
var movie = $("ul.menu li a.active");
if (movie.id == Gulliver || movie.id == null || movie.id == "") {
document.getElementsByClassName('window').video.source.src = Gulliver.mkv
}
else if (movie.id == Howl) {
document.getElementsByClassName('window').video.source.src = Howl.mkv
}
else if (movie.id == Kiki) {
document.getElementsByClassName('window').video.source.src = Kiki.mkv
}
else if (movie.id == Castle) {
document.getElementsByClassName('window').video.source.src = Castle.mkv
}
else if (movie.id == Totoro) {
document.getElementsByClassName('window').video.source.src = Totoro.mkv
}
else if (movie.id == Ponyo) {
document.getElementsByClassName('window').video.source.src = Ponyo.mkv
}
else if (movie.id == Mononoke) {
document.getElementsByClassName('window').video.source.src = Mononoke.mkv
}
else if (movie.id == Spirited) {
document.getElementsByClassName('window').video.source.src = Spirited.mkv
}
else if (movie.id == Sky) {
document.getElementsByClassName('window').video.source.src = Sky.mkv
}
else if (movie.id == Nausicaa) {
document.getElementsByClassName('window').video.source.src = Nausicaa.mkv
}
else (movie.id == Cat) {
document.getElementsByClassName('window').video.source.src = Cat.mkv
}
}
I'm not entirely sure this code is the best way to go about solving my problem, but it's the most logical progression I can think of.
This can all be condensed down considerably since most of the code stays the same in all cases. Also, your closing <ul> isn't a close tag and you are missing a closing <div>.
// Get all the <a> elements
var anchors = document.querySelectorAll(".menu a");
// Get a reference to the video element
var v = document.querySelector("video");
// Set up click event handlers for each
Array.prototype.slice.call(anchors).forEach(function(anchor){
anchor.addEventListener("click", function(evt){
// default video when no id is present
var d = "Gulliver.mkv";
// Use the default or the id
v.source = (!anchor.id) ? d : anchor.id + ".mkv";
console.log("video source is: " + v.source);
});
});
<div class="miyazaki">
<div class="menu">
<ul>
<li><a id="Gulliver">Gulliver's Travels</a></li>
<li><a id="Howl">Howl's Moving Castle</a></li>
<li><a id="Kiki">Kiki's Delivery Service</a></li>
<li><a id="Castle">Castle of Cagoliostro</a></li>
<li><a id="Totoro">"My Neighbor Totoro</a></li>
<li><a id="Ponyo">Ponyo</a></li>
<li><a>Porco Rosso</a></li>
<li><a id="Mononoke">"Princess Mononoke</a></li>
<li><a id="Spirited">Spirited Away</a></li>
<li><a id="Sky">The Sky's Castle Laputa</a></li>
<li><a id="Nausicaa">Nausicaa Valley of the Wind</a></li>
<li><a id="Cat">"The Cat Returns</a></li>
</ul>
</div>
</div>
<video></video>
As you also tagged jQuery here a working example for that:
$('.menu li a').on('click', function() {
var id = $(this).attr('id');
$('.window video source').attr('src', id + '.mkv');
});
Example
Note: Your html is invalid too. The ul is never closed
There's no need to use all these if blocks, you can do it in a one line statement.
You could just do it this way:
function Hayato() {
var movie = $("ul.menu li a.active").attr("id");
if(movie && movie !== "#")
document.getElementsByClassName('window')[0].video.source.src = movie + ".mkv";
}
Note:
In the code you used all the Strings are inavlid Strings, you should wrap them between two " or '.
I have this html where I need to render the data
default.hbs
<div class="chart-container" data-action="chartContainer">
<ul>
<li class="department">
<h3>Enterprise</h3>
<ul class="sections">
// HERE I NEED TO RENDER THE DATA IN AN <li> TAG
</ul>
</li>
</ul>
</div>
and here is the code
APP.chartContainer = (function () {
var Handlebars = window.Handlebars;
var bindEventsToUI = function () {
$.getJSON('maindata.json')
.done(function(data) {
localStorage.setItem('jsonData', JSON.stringify(data));
}).fail(function(err) {
console.log(err);
});
var chartContainerTemplate = $(".chart-container").html();
var theTemplate = Handlebars.compile(chartContainerTemplate);
var getData = localStorage.getItem('jsonData');
var iterateObj = $.each(JSON.parse(getData), function(key, val) {
return val;
});
var theCompiledHtml = theTemplate(iterateObj[0].enterprise);
$(".sections").append(theCompiledHtml);
};
var init = function (element) {
bindEventsToUI();
};
/**
* interfaces to public functions
*/
return {
init: init
};
}());
the function iterateObj returns this
[
{
"enterprise":[
{
"id":"10",
"name":"Hellen Quesada",
"role":"Principal Software Engineer"
},
{
"id":"11",
"name":"Jonathan Chavez",
"role":"Principal Creative Engineer"
}
]
},
{
"consumer":[
{
"id":"18",
"name":"Helga Martinez",
"role":"Production Manager"
},
{
"id":"19",
"name":"Leroy Bernard",
"role":"Sr. Software Engineer"
}
]
}
]
but all I need to render for now is the enterprise part of the data, that is why in my function I am doing iterateObj.[0].enterprise but I am not getting anything in the DOM yet, how do I iterate properly in the over the object in order to get the rendering of the data I need?
What am I missing ?
The template needs to be a script not html. The script can contain the needed html though.
<script id="foo" type="text/x-handlebars-template">
<div class="chart-container" data-action="chartContainer">
<ul>
<li class="department">
<h3>Enterprise</h3>
<ul class="sections">
//Not really sure what you want here
//But access the data like this
<li>{{enterprise.id}}</li>
</ul>
</li>
</ul>
</div>
</script>
Then you compile the template (ie the script):
//var chartContainerTemplate = $(".chart-container").html();
//rename the script to a better id
var chartContainerTemplate = $("#foo").html();
Lastly I would highly suggest reading the docs. There are ways of looping and accessing data. The above template is very basic.
I looked a lot, but I couldn't find a solution to my problem :c I'm not a JS expert, but this might be an easy one for programmers.
I have a list, a nav list:
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
</ul>
Two things:
I need to get the text (English) from <a> inside the <li>, subtract and return only the first 2 letters. I want the site to show En.
For the Português text, I want it to show Pt instead. A replace(); function should do, shouldn't it?
Can't insert neither id nor class into <a>, because it's generated by a plugin in Wordpress.
For the full code, the site is http://yogmel.com and the list on the upper right, on the navigation bar.
Note: The English site is not fully functional yet.
Thank you so much!
For you requirement, its better to maintain a mapping object instead.
jQuery
var langListMap = [{
text: "English",
shortName: "En"
}, {
text: "Português",
shortName: "Pt"
}]
$(document).ready(function(){
$.each($("a"), function(i,a){
var sName = getObject($(a).text());
if(sName){
$(a).text(sName.shortName);
}
});
})
function getObject(text){
console.log(text)
return langListMap.filter(function(a){
return a.text === text.trim();
})[0]
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Test
</a>
</li>
</ul>
Pure JS
var langListMap = [{
text: "English",
shortName: "En"
}, {
text: "Português",
shortName: "Pt"
}]
function getObject(text) {
return langListMap.filter(function(a) {
return a.text === text.trim();
})[0]
}
(function() {
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
var sName = getObject(a[i].innerHTML);
if (sName) {
a[i].innerHTML = sName.shortName;
}
}
})();
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Test
</a>
</li>
</ul>
To use plain JavaScript for this, rather than using the jQuery library, I'd suggest:
'use strict'
// declaring the variable 'abbreviationsMap' as a 'constant'
// to prevent rebinding later in the script (although it can
// still be updated, eg:
// abbreviationsMap.English = 'xy', or
// abbreviationsMap.Welsh = 'cy'
const abbreviationsMap = {
'English': 'En',
'Português': 'Pt'
};
// named function to handle the creation of abbreviations,
// with user-supplied options:
function abbreviations(opts) {
// declaring variables using 'let' to limit the
// variables to the scope of the block in which
// they're declared:
let settings = {
// the CSS selector by which the elements whose
// content should be abbreviated should be selected:
'selector': '.toAbbreviate',
// the map of full-words-to-abbreviations to use:
'abbreviations': abbreviationsMap
};
// iterating over the properties declared in the
// opts Object to update the settings:
for (let property in opts) {
// if the opts Object has a named property
// not inherited from its prototype equal to
// the current property-name:
if (opts.hasOwnProperty(property)) {
// we update the same property of the
// settings Object to match the value
// from the opts Object:
settings[ property ] = opts[ property ];
}
}
// finding the elements from the document that match the
// CSS selector:
let elements = document.querySelectorAll( settings.selector );
// converting that NodeList to an Array, using Array.from():
let elementsArray = Array.from( elements );
// iterating over the Array:
elementsArray.forEach(function(abbr) {
// 'abbr' the first-named argument of the anonymous function
// is automatically supplied by the function itself, and is
// a reference to the array-element of the array over which
// we're iterating.
// setting the title property of the array-element to
// offer an explanation of what the abbreviation means,
// removing any leading/trailing white-space using
// String.prototype.trim():
abbr.title = abbr.textContent.trim();
// updating the textContent of the element to match the
// abbreviation held in the settings.abbreviation Array,
// or, if that's a falsy value, simply returns the title
// of the element:
abbr.textContent = settings.abbreviations[abbr.title] || abbr.title;
});
}
// calling the function, and modifying the CSS selector
// to be used within the function:
abbreviations({
'selector': '.lang-item a'
});
'use strict'
const abbreviationsMap = {
'English': 'En',
'Português': 'Pt'
};
function abbreviations(opts) {
let settings = {
'selector': '.toAbbreviate',
'abbreviations': abbreviationsMap
};
for (let property in opts) {
if (opts.hasOwnProperty( property )) {
settings[property] = opts[ property ];
}
}
let elements = document.querySelectorAll( settings.selector );
let elementsArray = Array.from( elements );
elementsArray.forEach(function( abbr ) {
abbr.title = abbr.textContent.trim();
abbr.textContent = settings.abbreviations[ abbr.title ] || abbr.title;
});
}
abbreviations({
'selector': '.lang-item a'
});
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-cy" class="lang-item">
<a href="site.com/cy">
Welsh
</a>
</li>
</ul>
JS Fiddle demo.
Alternatively, another approach is:
'use strict'
// again, a named function to convert the textContent
// of relevant items into an abbreviated form:
function abbreviations(opts) {
let settings = {
// the CSS selector with which the elements should
// be selected
'selector': '.toAbbreviate',
// the property of the element, or its ancestor element,
// from which the abbreviation should be derived:
'property': 'id',
// the regular expression by which the abbreviation
// should be derived,
// this is looking for a sequence of lower-case
// alphabetical characters in the range from 'a' to 'z'
// ('[a-z]') inclusive, that's two characters in length
// ('{2}') and occurs at the end of the property-string
// ('$'):
'regexp': /[a-z]{2}$/
};
for (let property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
let elements = document.querySelectorAll(settings.selector);
let elementsArray = Array.from(elements),
text;
elementsArray.forEach(function(abbr) {
// finding the closest ancestor element to the current
// element that has the property identified in the
// settings.property value, using CSS attribute-selector
// syntax (identifying an 'id' property/attribute by '[id]'
// for example;
// accesing the property identified by the settings.property
// value of the found element, retrieving the 'id' by:
// HTMLElement[ id ],
// and retrieving the portion of the property-value
// that matches the settings.regexp property-value,
// (or null if there is no match):
text = abbr.closest('[' + settings.property + ']')[settings.property].match(settings.regexp);
// because null is a possible result, we check
// that the text variable exists:
if (text) {
// if it does, then we retrieve the match
// held at the first index of the returned
// Array:
text = text[0];
} else {
// otherwise we simply use the trimmed text-content:
text = abbr.textContent.trim();
}
// setting the title of the element:
abbr.title = abbr.textContent.trim();
// capitalising the first character of the String and
// concatenating that with the rest of the String:
abbr.textContent = text.charAt(0).toUpperCase() + text.slice(1);
});
}
abbreviations({
'selector': '.lang-item a'
});
'use strict'
function abbreviations(opts) {
let settings = {
'selector': '.toAbbreviate',
'property': 'id',
'regexp': /[a-z]{2}$/
};
for (let property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
let elements = document.querySelectorAll(settings.selector);
let elementsArray = Array.from(elements),
text;
elementsArray.forEach(function(abbr) {
text = abbr.closest('[' + settings.property + ']')[settings.property].match(settings.regexp);
if (text) {
text = text[0];
} else {
text = abbr.textContent.trim();
}
abbr.title = abbr.textContent.trim();
abbr.textContent = text.charAt(0).toUpperCase() + text.slice(1);
});
}
abbreviations({
'selector': '.lang-item a'
});
<ul id="menu-navbar">
<li id="menu-item-270-en" class="lang-item">
<a href="site.com/en">
English
</a>
</li>
<li id="menu-item-270-pt" class="lang-item">
<a href="site.com/">
Português
</a>
</li>
<li id="menu-item-270-cy" class="lang-item">
<a href="site.com/cy">
Welsh
</a>
</li>
</ul>
JS Fiddle demo.
This question already has an answer here:
Knockout observableArray not updating
(1 answer)
Closed 9 years ago.
My observablearrary "assignedbranches" contains objects when I run this page. But a that has foreach binding to the array is completely empty. Why?
var branchAssignment = function (data) {
this.branchNumber = data.BranchNumber;
this.branchName = data.BranchName;
this.branchlabel = data.BranchLabel;
}
var AppViewModel = function() {
var me = this;
me.assignedbranches = ko.observableArray([]);
me.LoadBranchAssignments = function () {
$.getJSON("api/branchassignments", function (data) {
var sorted = _.sortBy(data, function (b) {
return b.BranchName;
});
$.each(sorted, function (key, val) {
me.assignedbranches().push(new branchAssignment(val));
});
});
};
me.selectBranch = function (data, e) {
console.log(data);
};
me.selectedBranch = ko.observable("Baton Rouge (4001)");
};
var appv = new AppViewModel;
ko.applyBindings(appv);
HTML
<ul class="nav navbar-nav">
<li class="dropdown">
<span data-bind="text: selectedBranch()"></span> <b class="caret"></b>
<ul class="dropdown-menu" data-bind="foreach: assignedbranches ">
<li>
</li>
</ul>
</li>
</ul>
SAMPLE branchAssignment objects
[{"branchNumber":"4042","branchName":"Albuquerque","branchlabel":"Albuquerque (4042)"},{"branchNumber":"4006","branchName":"Alexandria","branchlabel":"Alexandria (4006)"},{"branchNumber":"1118","branchName":"Arden","branchlabel":"Arden (1118)"},{"branchNumber":"1113","branchName":"Ashland","branchlabel":"Ashland (1113)"},{"branchNumber":"4019","branchName":"Atlanta","branchlabel":"Atlanta (4019)"},{"branchNumber":"4030","branchName":"Austin","branchlabel":"Austin (4030)"},{"branchNumber":"1054","branchName":"Bakersfield","branchlabel":"Bakersfield (1054)"},{"branchNumber":"1115","branchName":"Baltimore","branchlabel":"Baltimore (1115)"},{"branchNumber":"4001","branchName":"Baton Rouge","branchlabel":"Baton Rouge (4001)"}]
Try:
me.assignedbranches.push(new branchAssignment(val));
instead of:
me.assignedbranches().push(new branchAssignment(val));
Javascript
var obj = {
"name" : ["alex","bob","ajhoge"],
"age" : [30,31,33]
};
to output "alex" for instance
document.write(obj["name"][0])
so how to filter through obj to fetch all data like
html
<ul>
<li>name
<ul>
<li>alex</li>
<li>bob</li>
<li>ajhoge</li>
</ul>
</li>
<li>age
<ul>
<li>30</li>
<li>31</li>
<li>33</li>
</ul>
</li>
</ul>
thank you
var x;
for (x in obj) {
if (obj.hasOwnProperty(x)) {
<li>x</ul>
<ul>
obj[x].forEach(function (elem) {
return "<li>" + elem + "</li>";
});
</ul>
}
}
You could work with something of this kind. Please note
Do not use an document.write
The inner loops inside the for are pseudo code.