How to build a tree control with JSON and jQuery - javascript

I have a web page on which I would like to display dynamically a tree based on a JSON array with the help of jQuery. Each node of my tree has a checkbox associated to it. When I click a node which has children, I would like all of them to be checked. I’ve already taken care of printing the tree and the checkboxes and I am now trying to select children nodes and I am not able.
Below is the code (simplified) that I have so far. Does anybody has an idea how I could automatically check the children checkboxes when a checkbox is checked with jQuery?
Thanks!
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript">
var myJSON = {
"d": {
"__type": "Branch",
"Name": "$",
"FullPath": "$\\$",
"SubBranch": [
{
"__type": "Branch",
"Name": "System",
"FullPath": "$\\System",
"SubBranch": [
{
"__type": "Branch",
"Name": "Library",
"FullPath": "$\\System\\Library",
"SubBranch": [
]
},
{
"__type": "Branch",
"Name": "Configuration",
"FullPath": "$\\System\\Configuration",
"SubBranch": [
{
"__type": "Branch",
"Name": "Reimage",
"FullPath": "$\\System\\Configuration\\Reimage",
"SubBranch": [
]
},
{
"__type": "Branch",
"Name": "Installation",
"FullPath": "$\\System\\Configuration\\Installation",
"SubBranch": [
]
}
]
},
]
}
]
}
}
var output;
var indentationLevel = 0;
function GetSpacing(numberOfSpaces) {
var tabs = '';
for (i = 0; i < numberOfSpaces; i++) {
tabs += ' ';
}
return tabs;
}
function GetHtmlForFeaturePath(featurePath) {
return '<div>' + GetSpacing(indentationLevel) + '<input type="checkbox" id="' + featurePath.FullPath + '" class="featureTreeCheckbox" />' + featurePath.Name + "</div>";
}
function GetHtmlForFeaturePaths(node) {
output += GetHtmlForFeaturePath(node);
indentationLevel++;
jQuery.each(node.SubBranch, function() {
GetHtmlForFeaturePaths(this);
});
indentationLevel--;
}
String.prototype.startsWith = function(str) {
return this.match("^" + str) == str;
}
window.onload = function() {
GetHtmlForFeaturePaths(myJSON.d);
document.writeln(output);
/* How do I tell a node to check its children? */
$('input').click(function(event) {
var clickedControlId = this.id;
alert(clickedControlId);
/* alert($.grep(myJSON.d, function (a) { return a.FullPath == clickedControlId })); */
});
}
</script>
</head>
<body>
jQuery
</body>
</html>

It is not a good practice to distinguish levels with spaces. Instead you should use a class or an id. This helps both the appearance (you can use css) and your code, since it defines logical levels.
Edit your code to produce a DOM like that:
<div class="level1">
<input id="$\$" class="featureTreeCheckbox" type="checkbox">$
<div class="level2">
<input id="$\System" class="featureTreeCheckbox" type="checkbox">System
<div class="level3">
<input id="$\System\Library" class="featureTreeCheckbox" type="checkbox">Library
</div>
<div class="level3">
<input id="$\System\Configuration" class="featureTreeCheckbox" type="checkbox">Configuration
<div class="level4">
<input id="$\System\Configuration\Reimage" class="featureTreeCheckbox" type="checkbox">Reimage<br/>
<input id="$\System\Configuration\Installation" class="featureTreeCheckbox" type="checkbox">Installation
</div>
</div>
</div>
</div>
Each "levelx" class defines a level. You can easily style it like this:
<style>
.level1 { margin-left: 0px; }
.level2 { margin-left: 10px; }
.level3 { margin-left: 20px; }
.level4 { margin-left: 30px; }
</style>
Then you can use code like this:
<script type="text/javascript">
$(function() {
$('div.level1 input').change(function(event) {
if ($(this).is(":checked")) {
$(this).parent().find("input").attr("checked", "checked");
}
});
$('div.level2 input').change(function(event) {
if ($(this).is(":checked")) {
$(this).parent().find(".level3 input").attr("checked", "checked");
$(this).parent().find(".level4 input").attr("checked", "checked");
}
});
$('div.level3 input').change(function(event) {
if ($(this).is(":checked")) {
$(this).parent().find(".level4 input").attr("checked", "checked");
}
});
});
</script>

I'd simplify what kgiannakakis did:
$(function() {
$('div input:first').change(function(event) {
if ($(this).is(":checked")) {
$(this).next("div").find("input").attr("checked", "checked");
} else {
$(this).next("div").find("input").removeAttr("checked");
}
});
});
This should work for any number of levels.

Related

Click evt is registered to div surrounding checkbox instead of the checkbox itself

I have a list of documents with a checkbox next to each. Clicking on a checkbox is supposed to append that document to a separate div (aka a "Favorites List"). This does not work, but clicking on the div that surrounds the checkbox appends that document correctly. Another issue is that clicking on a checkbox when the Favorites contains one or more documents clears the list.
How can I properly register the checkbox itself to the click event rather than the div surrounding the checkbox? I've tried different methods, such as
$( "input[type='checkbox']" ).on("click", faveFunc), but I've been coming up short so I thought I'd ask about it here.
JS snippet:
import $ from 'jquery';
var tableRes = '';
export default class {
constructor() {
this.loadTableData();
}
// this area contains code that's irrelevant //
// ------ Rendering checkboxes ------- //
$("#km-table-id tbody tr").on("click", function(evt) {
evt.stopPropagation();
if (evt.target.type !== "checkbox") {
$(":checkbox", this).on("click");
}
});
// ------ Appending checkboxes ------- //
let inputType = $("<input />", {"type": "checkbox"})
let chkboxCol = $("#km-table-id tbody tr > td:nth-child(3)");
chkboxCol.append(inputType).addClass("checkbox-class");
// --- My Favorites functionality ---- //
function faveFunc(evt) {
let anchor = $($(evt.target).prev().find("a")[0]).clone();
// let ancTxt = $(anchor).text();
switch($(".populate-faves").find("a:contains(" + $(anchor).text() + ")").length)
{
case 0:
$(".populate-faves").append(anchor);
break;
default:
$(".populate-faves > a:contains(" + $(anchor).text() + ")").remove();
break;
}
};
function newList() {
let data = $(evt.target).prev().find("a").eq(0).html();
let outputList = $(".populate-faves");
$(".populate-faves").html("");
$("#km-table-id tbody tr)").each(function(i, el) {
let cntxFave = $(".add-id", el);
let fave = $(".checkbox-class", el);
let itemText = $(data, el);
if(cntxFave.is(".add-id")) {
outputList.append("<li>" + itemText.html() + "</li>");
}
if(fave.prop("checked")) {
outputList.append("<li>" + itemText.html() + "</li>");
}
});
}; // ------------ newList
$(".checkbox-class").on("click", faveFunc);
HTML snippet:
<div class="col-md-14"> <!-- Right -->
<table id="km-table-id" class="cell-border display stripe-hover">
<thead>
<tr>
<!-- <th></th> -->
<th></th>
<th></th>
<th>Title</th>
<th></th> <!-- Keep here--this is for checkbox col -->
</tr>
</thead>
<tbody></tbody>
</table>
import $ from 'jquery';
import dt from 'datatables.net';
var categories = '';
var tableRes = '';
export default class {
constructor() {
this.loadCategoryData();
this.loadTableData();
}
let KMdocs = {
{
"d": {
"results": [
{
"__metadata": {
"id": "[redacted]",
"uri": "[redacted]",
"etag": "\"2\"",
"type": "[redacted]"
},
"File": {
"__metadata": {
"id": "[redacted]",
"uri": "[redacted]",
"type": "SP.File"
},
"Name": "Guide to Product IDs.docx"
},
"FileLeafRef": "Guide to Product IDs.docx",
"ResourceType": {
"__metadata": {
"type": "Collection(SP.Taxonomy.TaxonomyFieldValue)"
},
"results": [
{
"Label": "Guides \uff06 Protocols",
"TermGuid": "[redacted]",
"WssId": 706
}
]
},
"EncodedAbsUrl": "[redacted]"
},
{
"__metadata": {
"id": "[redacted]",
"uri": "[redacted]",
"etag": "\"3\"",
"type": "SP.Data.KMDocumentsItem"
},
"File": {
"__metadata": {
"id": "[redacted]",
"uri": "[redacted]",
"type": "SP.File"
},
"Name": "LRRP Template 1.docx"
},
"FileLeafRef": "LRRP Template 1.docx",
"ResourceType": {
"__metadata": {
"type": "Collection(SP.Taxonomy.TaxonomyFieldValue)"
},
"results": [
{
"Label": "Templates",
"TermGuid": "[redacted]",
"WssId": 941
},
{
"Label": "Guides \uff06 Protocols",
"TermGuid": "[redacted]",
"WssId": 706
}
]
},
"EncodedAbsUrl": "[redacted]"
},
{
"__metadata": {
"[redacted]",
"uri": "[redacted]",
"etag": "\"3\"",
"type": "SP.Data.KMDocumentsItem"
},
"File": {
"__metadata": {
"id": "[redacted]",
"uri": "[redacted]",
"type": "SP.File"
},
"Name": "LRRP Template 2.docx"
},
"FileLeafRef": "LRRP Template 2.docx",
"ResourceType": {
"__metadata": {
"type": "Collection(SP.Taxonomy.TaxonomyFieldValue)"
},
"results": [
{
"Label": "Templates",
"TermGuid": "[redacted]",
"WssId": 941
},
{
"Label": "Guides \uff06 Protocols",
"TermGuid": "[redacted]",
"WssId": 706
}
]
},
"EncodedAbsUrl": "[redacted]"
}
]
}
}
}
// ------ Loading Category data ------ //
loadCategoryData() {
let res = KMdocs.d.results.filter(function(val) {
return (val.FileLeafRef.trim().length > 0);
}).map(function(obj) {
return {
"FileName": obj.FileLeafRef,
"Titles": obj.File.Name,
"Path": obj.EncodedAbsUrl,
"Categories": obj.ResourceType.results.map(function(val) {
return val.Label;
}).join(";")
};
});
let label = KMdocs.d.results.filter(function(val) {
return (val.FileLeafRef.trim().length > 0);
}).map(function(obj) {
return obj.ResourceType.results.map(function(val) {
return val.Label;
})
});
// ---------- Unique Categs. --------- //
let unique = [];
let temp = KMdocs.d.results.filter(function(val) {
return (val.FileLeafRef.trim().length > 0);
}).forEach(function(obj) {
obj.ResourceType.results.forEach(function(val) {
let divCat = document.createElement("div");
$(divCat).attr("category", encodeURIComponent(val.Label));
$(divCat).html(val.Label);
if (!unique.includes(divCat.outerHTML)) {
unique.push(divCat.outerHTML); // value can be anything, only keys matter
}
})
});
let categories = unique.sort();
$(".indiv-label").append(categories);
} // ------------- loadCategoryData()
// ------ Loading doc title data ----- //
loadTableData() {
// Local icons exist under /SiteAssets/images, if needed //
function docType(fileName) {
let docImg = "<img src='[redacted]/Current.ashx/docx.gif' />"
let msgImg = "<img src='[redacted]/Current.ashx/msg.gif' />"
let nrlImg = "<img src='[redacted]/Current.ashx/nrl.gif' />"
let pdfImg = "<img src='[redacted]/Current.ashx/pdf.gif' />"
let pptImg = "<img src='[redacted]/Current.ashx/pptx.gif' />"
let xlsImg = "<img src='[redacted]/Current.ashx/xls.gif' />"
let docStr = fileName.split(".") // .split() seems to be necessary to render the icons
for (var i = 0; i < docStr.length; i++) {
if (docStr[i].includes('doc')) {
return docStr[i] = docImg;
} // -
else if (docStr[i].includes('DOCX')) {
return docStr[i] = docImg;
} // -
else if (docStr[i].includes('rtf')) {
return docStr[i] = docImg;
} // -
else if (docStr[i].includes('msg')) {
return docStr[i] = msgImg;
} //
else if (docStr[i].includes('nrl')) {
return docStr[i] = nrlImg;
} //
else if (docStr[i].includes('pdf')) {
return docStr[i] = pdfImg;
} //
else if (docStr[i].includes('ppt')) {
return docStr[i] = pptImg;
} // -
else if (docStr[i].includes('PPT')) {
return docStr[i] = pptImg;
} // -
else if (docStr[i].includes('potx')) {
return docStr[i] = pptImg;
} // -
else if (docStr[i].includes('xls')) {
return docStr[i] = xlsImg;
} //
}
} // docType
$.noConflict();
let tableRes = KMdocs.d.results.filter(function(val) {
return (val.FileLeafRef.trim().length > 0);
}).map(function(obj) {
return {
"Path": obj.EncodedAbsUrl,
"Titles": obj.File.Name,
"Categories": obj.ResourceType.results.map(function(val) {
return val.Label;
}).join(";"),
"Blank": "", // use to create an empty column, if necessary
"docImg": docType(obj.File.Name) // Icon
}
})
// --------- Rendering table --------- //
$('#km-table-id').DataTable({
data: tableRes,
columns: [{
data: "Categories"
}, // available but hidden
{
data: "docImg",
sortable: false
}, // hides sorting arrows in icon col
{
data: "Titles"
},
{
data: "Blank",
sortable: false
}
],
columnDefs: [{
data: "Path",
ordering: true,
targets: [2],
render: function(data, type, row) {
return $('<a>')
.attr({
target: "_blank",
href: row.Path
})
.text(data)
.wrap('<div></div>')
.parent()
.html();
},
},
{
searchable: true,
targets: [0],
visible: false
}, // hides Categories col
],
language: {
searchPlaceholder: "Search All Documents"
},
lengthMenu: [10, 25, 50, 100, 250, 500],
order: [],
pageLength: 500, // showing multiple pgs doesn't render all checkboxes...but initially showing all items renders them
paging: true,
pagingType: "full_numbers",
responsive: true,
scrollCollapse: true,
scrollXInner: true,
scrollY: 550,
sDom: '<"top">rt<"bottom"flp><"left">' // affixes dropdown on bottom
});
// ------ Rendering checkboxes ------- //
$("#km-table-id tbody tr").on("click", function(evt) {
evt.stopPropagation();
if (evt.target.type !== "checkbox") {
$(":checkbox", this).on("click");
}
});
// ------ Appending checkboxes ------- //
let inputType = $("<input />", {
"type": "checkbox"
})
let chkboxCol = $("#km-table-id tbody tr > td:nth-child(3)");
chkboxCol.append(inputType).addClass("checkbox-class");
// --- My Favorites functionality ---- //
function faveFunc(evt) {
let anchor = $($(evt.target).prev().find("a")[0]).clone();
switch ($(".populate-faves").find("a:contains(" + $(anchor).text() + ")").length) {
case 0:
$(".populate-faves").append(anchor);
break;
default:
$(".populate-faves > a:contains(" + $(anchor).text() + ")").remove();
break;
}
};
function newList() {
let data = $(evt.target).prev().find("a").eq(0).html();
let outputList = $(".populate-faves");
$(".populate-faves").html("");
$("#km-table-id tbody tr)").each(function(i, el) {
let cntxFave = $(".add-id", el);
let fave = $(".checkbox-class", el);
let itemText = $(data, el);
if (cntxFave.is(".add-id")) {
outputList.append("<li>" + itemText.html() + "</li>");
}
if (fave.prop("checked")) {
outputList.append("<li>" + itemText.html() + "</li>");
}
});
}; // ------------ newList
$(":checkbox").on("click", faveFunc);
$("#add-id").on("click", faveFunc); // does not work
// Linking custom search w/ DT search //
let oTable = $("#km-table-id").DataTable();
$("#searchbar").on("input", function() {
oTable.search($(this)
.val() + " " + decodeURIComponent($(this)
.attr("category"))).draw();
})
} // ------------------ loadTableData
} // ------------- export default class
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1, shrink-to-fit=no">
<title>Pages - KM</title>
<meta name="description" content="description here">
<meta name="keywords" content="keywords,here">
<!-- DataTables CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/select/1.2.7/css/select.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/fixedheader/3.1.5/css/fixedHeader.dataTables.min.css">
<link rel="stylesheet" href="KMStyles.css" type="text/css">
<!-- jQuery first, then Popper.js -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<!------------------------------->
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js" defer></script>
<script type="text/javascript" src="https://cdn.datatables.net/select/1.2.7/js/dataTables.select.min.js"></script>
<!------------------------------->
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.6.2/core.min.js"></script>
<script type="text/javascript" src="SiteAssets/scripts/getListItems.js"></script>
</head>
<body>
<script src="./bundle.js"></script>
<div class="km-div-container">
<div class="col-md-3">
<!-- Left -->
<span class="KM-title"><h1>KM</h1></span>
<div class="form-group">
<input category="" class="form-control" daysprior="" id="searchbar" input-all="" placeholder="Search All Documents..." type="search">
</div>
<div id="myFave.hs-gc-header" class="faves-div">
<p style="font-weight:bold">My Favorites:</p>
<div class="populate-faves"></div>
<!-- location of favorited documents -->
</div>
</div>
<!-------------------------------------------->
<div class="col-md-3" id="middle-id">
<!-- Middle -->
<p>
<div class="all-docs-title" category="" style="font-weight:bold; cursor:pointer" input="">All Documents</div>
</p>
<p>
<div class="recently-added-title" category="" days="30" style="cursor:pointer;">Recently Added and/or Modified</div>
</p>
<div id="km-labels">
<a>
<p class="indiv-label" style="cursor:pointer;"></p>
</div>
</div>
<!-------------------------------------------->
<div class="col-md-14">
<!-- Right -->
<table id="km-table-id" class="cell-border display stripe-hover">
<thead>
<tr>
<!-- <th></th> -->
<th></th>
<th></th>
<th>Title</th>
<th></th>
<!-- Keep here--this is for checkbox col -->
</tr>
</thead>
<tbody></tbody>
</table>
<!-- <ul class="custom-menu">
<li data-action="open" id="open-id">Open Document</li>
<li data-action="add" id="add-id">Set As Favorite</li>
<li data-action="email">Email Document</a></li>
</ul> -->
</div>
<!-- col-md-14 -->
<!-- <div class="kmdialog"></div> -->
<!-- what is this? -->
</div>
<!-- km-div-container -->
</body>
</html>
The core of the demo is the following:
event.type ⇩ Event Handler ⇩
$(document).on('change', 'CHECKBOX', addFav);
⇧ event.currentTarget ⇧ event.target
Demo Outline
Assign each checkbox a data-* attribute with a unique value. ex. data-id="1"
To reference and clone an element in a table cell placed before the table cell with the checkbox, ex.:
$(event.target).closest('td').prev('td').find(ANCHOR).clone();
Use the data-* attribute of the checkbox to associate it with the cloned element in the example below the cloned anchor is appended to a <li>, ex.:
$(LI).addClass('fav'+ID).append(ANCHOR);
The association is used to remove the <li> from the favorites list when the checkbox is unchecked, ex.:
$('.fav'+ID).remove();
Review demo in full page mode
<!DOCTYPE html>
<html>
<head>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.css" rel="stylesheet">
<style>
caption {
caption-side: top;
font-size: 1.5rem;
font-weight: 400
}
.favorite {
list-style: none
}
.favorite li::before {
content: '\1f49a\a0\a0'
}
</style>
</head>
<body>
<header class="container">
<section class='row'>
<fieldset class="col-md-12">
<legend>💖 Favorites:</legend>
<ul class='favorite'></ul>
</fieldset>
</section>
</header>
<hr>
<main class='container'>
<section class='row'>
<article class="col-md-12">
<table class='table'>
<caption>Data</caption>
<thead>
<tr>
<th></th>
<th>Link</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody class='table-bordered'>
<tr>
<td></td>
<td>
<a href='#/' class='lnx'>LINK 1: Category I</a></td>
<td>
<label class="custom-control custom-checkbox">
<input class="chx custom-control-input" type="checkbox" data-id='1'>
<b class="custom-control-indicator"></b>
<b class="custom-control-description"> 💙</b>
</label>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<a href='#/' class='lnx'>LINK 2: Category II</a></td>
<td>
<label class="custom-control custom-checkbox">
<input class="chx custom-control-input" type="checkbox" data-id='2'>
<b class="custom-control-indicator"></b>
<b class="custom-control-description"> 💙</b>
</label>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<a href='#/' class='lnx'>LINK 3: Category III</a></td>
<td>
<label class="custom-control custom-checkbox">
<input class="chx custom-control-input" type="checkbox" data-id='3'>
<b class="custom-control-indicator"></b>
<b class="custom-control-description"> 💙</b>
</label>
</td>
<td></td>
</tr>
</tbody>
</table>
</article>
</section>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).on('change', '.chx', addFav);
function addFav(e) {
var tgt = e.target;
var ID = $(tgt).data('id');
if (tgt.checked) {
var lnx = $(tgt).closest('td').prev('td').find('.lnx').clone();
var item = document.createElement('li');
$('.favorite').append(item);
$(item).addClass('fav' + ID).append(lnx);
return false;
} else if (!tgt.checked) {
$('.fav' + ID).remove();
}
}
</script>
</body>
</html>

How append a json data in select box with optgroup

I am a newbie in jquery. I want to display JSON data to my select box. My JSON Data is
{
"Color":[
{
"Id":"1",
"Attrib_name":"Color",
"Attrib_value":"Red"
},
{
"Id":"2",
"Attrib_name":"Color",
"Attrib_value":"Blue"
}
],
"Size":[
{
"Id":"3",
"Attrib_name":"Size",
"Attrib_value":"5.6"
},
{
"Id":"4",
"Attrib_name":"Size",
"Attrib_value":"5.1"
}
]
}
I want to create optgroup with option based on the above json. i.e
<optgroup label="color">
<option>Red</option>
<option>Blue</option>
</optgroup>
<optgroup label="size">
<option>5.6</option>
<option>5.1</option>
</optgroup>
I am stuck on how to start. Please help me to get rid of this.
Just take the result of this code and paste it as HTML
What it does is iterate over your json object and create the elements.
var json = {
"Color":[
{
"Id":"1",
"Attrib_name":"Color",
"Attrib_value":"Red"
},
{
"Id":"2",
"Attrib_name":"Color",
"Attrib_value":"Blue"
}
],
"Size":[
{
"Id":"3",
"Attrib_name":"Size",
"Attrib_value":"5.6"
},
{
"Id":"4",
"Attrib_name":"Size",
"Attrib_value":"5.1"
}
]
}
console.log(
Object.keys(json).map(a=>`<optgroup label="${a}">${json[a].map(b=>`<option>${b.Attrib_value}</option>`).join``}</optgroup>`)
)
Here is a full example of how you can do this. You just have to build your HTML and use a jQuery function to add it in the html file. Check my codepen. Code is also bellow
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
type="text/javascript"></script>
<select id="mySelect"></select>
<script type="text/javascript">
var data = {
"Color":[
{
"Id":"1",
"Attrib_name":"Color",
"Attrib_value":"Red"
},
{
"Id":"2",
"Attrib_name":"Color",
"Attrib_value":"Blue"
}
],
"Size":[
{
"Id":"3",
"Attrib_name":"Size",
"Attrib_value":"5.6"
},
{
"Id":"4",
"Attrib_name":"Size",
"Attrib_value":"5.1"
}
]
};
var html = "";
for(var option in data){
html += `<optgroup label="`+ option +`">`;
data[option].forEach(function(item){
html += `<option value="` + item["Attrib_value"] + `" >`+
item["Attrib_value"] +`</option>`
});
html += `</optgroup>`;
}
console.log('html',html);
$('#mySelect').html(html);
</script>
Since jQuery allows a nice api to create dom elements so I would use the following approach but other answer will work:
var data = {
"Color":[
{
"Id":"1",
"Attrib_name":"Color",
"Attrib_value":"Red"
},
{
"Id":"2",
"Attrib_name":"Color",
"Attrib_value":"Blue"
}
],
"Size":[
{
"Id":"3",
"Attrib_name":"Size",
"Attrib_value":"5.6"
},
{
"Id":"4",
"Attrib_name":"Size",
"Attrib_value":"5.1"
}
]
};
var select = $('<select/>');
$.each(data, function(g, colors) {
var group = $('<optgroup/>', {label:g});
$.each(colors, function(i, color) {
var option = $('<option/>', {
value: color.Attrib_value,
text: color.Attrib_value
});
select.append(group.append(option));
});
});
$('#dropdown').append(select);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='dropdown'></div>

Angular JS - Parent and Child Node - Handling menu and sub-menu

Please help me with the below code. I have created this so that anyone can make changes and give me a solution.
HTML
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="continent in destinations">
<input type="checkbox" ng-model="continent.selected" ng-change="parentChange($index)"> {{continent.name}} ({{continent.countries_selected}}
/ {{continent.countries.length}}) - {{continent.all_selected}}
<ul>
<li ng-repeat="country in continent.countries">
<input type="checkbox" ng-model="country.selected"> {{country.name}} </li>
</ul>
</li>
</ul>
<p>You have selected: {{total_selected}} Countries in Total</p>
</body>
</html>
Controller
app.controller('MainCtrl', function($scope) {
$scope.total_selected = 0;
$scope.$watch('destinations', function(destinations){
var total_selected = 0;
angular.forEach(destinations, function(continent){
continent.countries_selected = 0;
angular.forEach(continent.countries, function(country){
total_selected += country.selected ? 1 : 0
continent.countries_selected += country.selected ? 1 : 0
if (continent.countries_selected == continent.countries.length) {
continent.selected = true;
} else {
continent.selected = false;
}
});
});
$scope.select_all = function(continent){
continent.selected = true;
}
$scope.total_selected = total_selected;
}, true);
$scope.select = function(continent){
console.log(continent)
continent.selected = true;
}
$scope.parentChange = function(index) {
angular.forEach( $scope.destinations[index].countries, function(country) {
country.selected = $scope.destinations[index].selected;
});
};
$scope.destinations = [
{
"name": "India",
"countries": [
{
"name": "Mumbai"
},
{
"name": "Delhi"
},
{
"name": "Calicut"
}
]
},
{
"name": "America - US",
"countries": [
{
"name": "New York"
},
{
"name": "Canada"
},
{
"name": "Miami"
},
{
"name": "Hackensack"
}
]
}
];
});
Fiddle Link
What I want to do is:
For Ex: If I select a child node (Delhi) then the parent node (India) should be checked automatically.
In other words, any child node checked should immediately check the parent node and vice-versa
Thanks,
Kimz.
It's a pretty simple change, right now you're watching the collection for changes and doing this inside of it
if (continent.countries_selected == continent.countries.length) {
continent.selected = true;
} else {
continent.selected = false;
}
Whereas what you really want is if at least 1 country is selected for that continent to be selected, so just change it to
if (continent.countries_selected > 0) {
continent.selected = true;
} else {
continent.selected = false;
}
Fiddle For Example

How to set Check attribute for a radio element?

I'm trying to make a quiz app using JavaScript and jQuery, if I click on a radio button am setting an attribute as checked but if I move on to another question and click on any radio button there in the previous question radio button selects get removed how can I make sure that it is selected even if I move on to any question and select options there. I guess its happening because I have common name on the radio button Can someone help me out?
$(document).ready(function() {
$(function() {
var data = [{
"id": 1,
"question": "Which framework is best?",
"options": ["ReactJs", "AngularJs", "Vue.js", "Backbone"],
"correctanswer": "ReactJs",
"selectedanswer": ""
},
{
"id": 2,
"question": "What does MVC stands for?",
"options": ["Model View Controller", "Model view view Controller", "Model Controller view", "None of the above"],
"correctanswer": "Model View Controller",
"selectedanswer": ""
},
{
"id": 3,
"question": "Which is the best MOOC course website?",
"options": ["Coursera", "EDX", "Udacity", "Code School"],
"correctanswer": "Udacity",
"selectedanswer": ""
},
{
"id": 4,
"question": "which backend framework is not available in any of the MOOC site?",
"options": ["Ruby on Rails", "Node JS", "Metor Js", "Django"],
"correctanswer": "Django",
"selectedanswer": ""
},
{
"id": 5,
"question": "Which frontend framework is not available?",
"options": ["AngularJs", "ReactJs", "Backbone", "Knockout"],
"correctanswer": "ReactJs",
"selectedanswer": ""
}
];
var constructordata = function(data) {
this.id = data.id,
this.question = data.question,
this.options = data.options,
this.correctanswer = data.correctanswer,
this.selectedanswer = data.selectedanswer
};
var datas = [];
data.forEach(function(item) {
datas.push(new constructordata(item));
});
var controller = {
nextquestion: function() {
var currentelement = $('#app .questionsection.visible');
var nextelement = currentelement.next();
if (nextelement.hasClass('questionsection')) {
currentelement.removeClass('visible');
nextelement.addClass('visible');
}
if (!nextelement.next().hasClass('questionsection')) {
$("button").addClass('visible');
}
},
previousquestion: function() {
var currentelement = $('#app .questionsection.visible');
var previouselement = currentelement.prev();
if (previouselement.hasClass('questionsection')) {
currentelement.removeClass('visible');
previouselement.addClass('visible');
}
},
selectanswers: function(temp) {
for (var i = 0; i < datas.length; i++) {
if (datas[i].id == temp) {
datas[i].selectedanswer = $('input[name=optradio]:checked').val();
}
}
},
checkanswers: function() {
var score = 0;
datas.forEach(function(item) {
if (item.selectedanswer == item.correctanswer) {
score++;
}
});
console.log(score);
},
init: function() {
view.init();
}
};
var view = {
init: function() {
this.maintemplate = $("#app");
this.nextarrow = $("#next");
this.submit = $("button");
this.submit.on('click', function() {
controller.checkanswers();
})
this.nextarrow.on('click', function() {
controller.nextquestion();
});
this.previousarrow = $("#previous");
this.previousarrow.on('click', function() {
controller.previousquestion();
});
this.render();
},
render: function() {
var maintemplate = this.maintemplate;
for (var i = 0; i < datas.length; i++) {
maintemplate.append("<div class='questionsection'><p>" + datas[i].question + "</p></div>");
for (var j = 0; j < datas[i].options.length; j++) {
var options = "<div class='radio'><label><input type='radio' name='optradio' questionno=" + datas[i].id + " value=" + datas[i].options[j] + ">" + datas[i].options[j] + "</label></div>"
maintemplate.children('.questionsection').last().append(options);
}
}
maintemplate.children().first().addClass('visible');
var radio = $("input[name=optradio]");
radio.on('click', function() {
var temp = $(this).attr("questionno");
controller.selectanswers(temp);
$(this).attr("checked", "checked");
});
}
};
controller.init();
});
});
.questionsection {
display: none;
}
.btn {
display: none;
}
.visible {
display: block;
}
i.fa {
font-size: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-2">
<i id="previous" class="fa fa-arrow-left" aria-hidden="true"></i>
</div>
<div id="app" class="col-md-8">
</div>
<div class="col-md-2">
<i id="next" class="fa fa-arrow-right" aria-hidden="true"></i>
</div>
</div>
<button class="btn">Submit</button>
</div>
CodePen http://codepen.io/kannant14/pen/peLOXG
One way to solve it is to name your radiobuttons in the render function with a question index like:
var options="<div class='radio'><label><input type='radio' name='optradio"+i+"' questionno="+datas[i].id+" value="+datas[i].options[j]+">"+datas[i].options[j]+"</label></div>"
and the same in the selectanswers function:
selectanswers:function(temp) {
for(var i=0;i<datas.length;i++) {
if(datas[i].id==temp){
datas[i].selectedanswer=$('input[name=optradio'+i+']:checked').val();
}
}
}
then you just need to keep track of the question index when checking the correct answers.

Angular / javascript product filter

I want to modify this product angular / javascript filter
When user select for example: Processor - 2GHz and Memory - 32GB I want to show only product that have all those properties.
Right now filter shows all products with 2GHz and all products with 32GB.
Plunker
https://plnkr.co/edit/vr9o2aHkHvCL1cvgKILd?p=preview
angular.module('app', [])
.controller('Controller', function($scope) {
$scope.items = [{
"name": "Product - 2GHZ, 32GB, Black",
"tags": [{
"category": 1,
"tag": 2
}, {
"category": 2,
"tag": 4
}, {
"category": 3,
"tag": 8
}]
}, {
"name": "Product - 2GHz, 128GB, Black",
"tags": [{
"category": 1,
"tag": 2
}, {
"category": 2,
"tag": 5
}, {
"category": 3,
"tag": 8
}]
}, {
"name": "Product - 1GHz, 128GB, White",
"tags": [{
"category": 1,
"tag": 1
}, {
"category": 2,
"tag": 5
}, {
"category": 3,
"tag": 7
}]
}];
$scope.items_dup = $scope.items
// checkbox selection
$scope.selectionTag = [];
$scope.selectionCat = [];
$scope.toggleSelection = function toggleSelection(tag, category) {
var idxTag = $scope.selectionTag.indexOf(tag);
if (idxTag > -1) {
$scope.selectionTag.splice(idxTag, 1);
} else {
$scope.selectionTag.push(tag);
}
// this is not working, probably need twodimensional array
// category
var idxCat = $scope.selectionCat.indexOf(category);
if (idxCat > -1) {
$scope.selectionCat.splice(idxCat, 1);
} else {
$scope.selectionCat.push(category);
}
};
// filter list
$scope.filter = function() {
filterTag($scope.selectionTag, $scope.items);
function filterTag(selected, items) {
var out = [];
angular.forEach(items, function(value, key) {
angular.forEach(selected, function(inner_value, key) {
angular.forEach(value.tags, function(inner_value2, key) {
if (value.tags[key].tag === inner_value) {
if (out.indexOf(value) == -1) {
out.push(value)
}
}
})
})
})
if (out.length > 0) {
$scope.items_dup = out;
} else {
$scope.items_dup = $scope.items;
}
}
};
})
<!DOCTYPE html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="Controller">
<h1>Product filtering!</h1>
<li ng-repeat="item in items_dup">
{{item.name}}
</li>
<hr>
<p data-category="1">Processor</p>
<label>
<input type="checkbox" value="1" ng-click="toggleSelection(1,1)"> 1GHz
</label>
<br>
<label>
<input type="checkbox" value="2" ng-click="toggleSelection(2,1)"> 2GHz
</label><br>
<hr>
<p data-category="2">Memory</p>
<label>
<input type="checkbox" value="4" ng-click="toggleSelection(4,2)"> 32GB
</label>
<br>
<label>
<input type="checkbox" value="5" ng-click="toggleSelection(5,2)"> 128GB
</label><br>
<hr>
<p data-category="3">Color</p>
<label>
<input type="checkbox" value="7" ng-click="toggleSelection(7,3)"> White
</label>
<br>
<label>
<input type="checkbox" value="8" ng-click="toggleSelection(8,3)"> Black
</label><br>
<br><br>
<button ng-click="filter()">Filter list</button>
</div>
</body>
</html>
You could use Array#every to check if each selection appears in an item's tag list. If you want to switch back from an and to an or filter, you can use Array#some instead.
The good thing about these methods is that they return as soon as they know their result, ending the loop early when possible.
$scope.filter = function() {
$scope.items_dup = $scope.items.filter(function(item) {
return $scope.selectionCat.every(function(c) {
return item.tags
.some(function(t) {
return t.category === c &&
$scope.selectionTag.indexOf(t.tag) !== -1;
});
})
});
}
In a plunker: here
An example of how you could create a map, and how it would affect your filter:
var $scope.items = [ /* ... */ ].map(function(item) {
var tagMap = item.tags.reduce(function(result, t.tag) {
result[t.tag] = true; // Add the tag Id to the map
return result;
}, {});
// Add the tag map to the item
return Object.assign(item, { tagMap: tagMap });
});
// Now, you can filter like so:
var matches = selectedTagIds.every(function(id) {
return item.tagMap[id];
});
another approach is using filter with filter object on ng-repeat, in this case items are filtered immediately when check boxes state changes:
<li ng-repeat="item in items_dup | filter : mainFilter : true">
{{item.name}}
</li>
all check boxes use ng-model and trigger update function:
<label>
<input type="checkbox" ng-change="updateFilters()" ng-model="filters.processor['1GHZ']"> 1GHz
</label>
update function:
$scope.updateFilters = function updateFilters() {
$scope.mainFilter = {};
angular.forEach($scope.filters, function (category, categoryKey) {
angular.forEach(category, function (value, valueKey) {
if (value) {
if ($scope.mainFilter[categoryKey]) {
delete $scope.mainFilter[categoryKey];
}
else {
$scope.mainFilter[categoryKey] = valueKey;
}
}
});
});
};
each item has processor, memory and color attributes added.
plunker: https://plnkr.co/edit/6BDVYzV430N0MSaopGXt?p=preview

Categories