Change content of a div based on an array within an array - javascript

Pastebin of index.html: http://pastebin.com/kdKFqTxe
Just copy and paste that and run it (this works but with some broken img links & no css).
With regards to the pastebin, just click on a node, and then click the first broken image below the video. What should happen is a dialogue box should appear with links to articles (from tubeArray). All relevant code is pasted below.
I'm trying to dynamically change the contents of a div when I click an image. The image has it's respective id (the first index in the inner array) within the first inner array there's another array (index 3). I want to populate my div (id="articleLinks") with those links using JQuery when the image is clicked.
JavaScript & JQuery:
The tube array. *Note: the first index of each element in tubeArray is the ID & the news articles aren't linked to anything particular. Only interested in tubeArray[0] & tubeArray[4]
var tubeArray = [
['UQ', -27.495134, 153.013502, "http://www.youtube.com/embed/uZ2SWWDt8Wg",
[
["example.com", "Brisbane students protest university fee hikes"],
["example.com", "Angry protests over UQ student union election"],
]
],
['New York', 40.715520, -74.002036, "http://www.youtube.com/embed/JG0wmXyi-Mw",
[
["example.com" , "NY taxpayers’ risky Wall Street bet: Why the comptroller race matters"]
]
],
['To The Skies', 47.09399, 15.40548, "http://www.youtube.com/embed/tfEjTgUmeWw",
[
["example.com","Battle for Kobane intensifies as Islamic State uses car bombs, Syrian fighters execute captives"],
["example.com","Jihadists take heavy losses in battle for Syria's Kobane"]
]
],
['Fallujah', 33.101509, 44.047308, "http://www.youtube.com/embed/V2EOMzZsTrE",
[
["example.com","Video captures family cat saving California boy from dog attack"],
["example.com","Fines of £20,000 for dogs that chase the postman"]
]
]
];
A for loop which goes through each element in tubeArray then assigns id to the first index. Also an image that calls the function myFunctionId which takes the parameter this.id.
for (i = 0; i < tubeArray.length; i++) {
var id = tubeArray[i][0];
//other code
'<img src="img.png" onclick="myFunctionId(this.id);" id="' + id + '">' +
//other code
}
function myFunctionId (id) {
journal = id;
alert(journal) //just a test
//I want to search through tubeArray with the id and find the matching inner array.
//I then want to loop through the innerArray and append to my html a link using JQuery.
$('#articleLinks').append("<a href='"+innerArray[0]+"'>"+innerArray[1]+'</a>'); // use CSS to break lines
}
}
HTML:
<div id="articleLinks">
Example Link<br>
</div>
Any help would be greatly appreciated. I've tried to simplify & cut out as much as I can so it's readable.

try this...
function myFunctionId (id) {
console.log(tubeArray);
tubeArray.forEach(function(entry) {
if (entry[0]==id) {
entry[4].forEach(function(innerArray){
$('#articleLinks').append("<a href='"+innerArray[0]+"'>"+innerArray[1]+'</a>'); // use CSS to break lines
});
return;
}
});
}
it makes it look like this for me... you're gonna have to handle that encoding issue. with the apostrophe. there are a lot of ways to handle it...
so.... if it was me... which it's not. but if it was... i would use an associative array instead of a numerically indexed one because it's easier to read the code and understand what you're using and where and how and things and stuff.
tubeArray = {
'UQ' : { 'location': [-27.495134, 153.013502],
'youtube': "example.com/embed/uZ2SWWDt8Wg",
'articles': [["example.com/queensland/brisbane-students-protest-university-fee-hikes-20140521-zrk8o.html", "Brisbane students protest university fee hikes"],
["example.com/content/2012/s3578878.htm", "Angry protests over UQ student union election"], ]
},
'New York': { 'location': [0.715520, -74.002036],
'youtube': "example.com/embed/JG0wmXyi-Mw",
'articles': [["example.com/2014/10/19/ny-taxpayers-risky-wall-street-bet-why-the-comptroller-race-matters/" , "NY taxpayers’ risky Wall Street bet: Why the comptroller race matters"]],
},
}

Related

How to dynamically populate materialize material_chip data and add extra data to chips?

I would like to have several chip inputs on page, each of these fields has a separate list of tags. Also I want the tags to carry some extra data in them like the id of the referenced object (more on this later).
To figure out what tag list should be used for each of the chips inputs I wanted to use the actual input, i.e.:
<div class="chips" data-taglist-id="10"></div>
<div class="chips" data-taglist-id="21"></div>
data-taglist-id is of course the index in some array of tags, i.e.:
var tags = [
0: [...],
[...]
10: [{tag: 'one'}, {tag: 'two'}],
[...]
21: [{tag: 'another'}, {tag: 'different'}]
];
so instead of delivering array to materialize_chips like that:
$('.chips').material_chip({
data: tags[0]
});
I'd like something like that (code below is just for illustration as it does not work):
$('.chips').material_chip({
data: function(this) {
var taglistId = $(this).data('taglist-id');
return tags[taglistId];
}
});
Another thing - I would like each tag in the chips to have some data attribute(s), so instead of being an entry like this:
<div class="chip">Apple<i class="material-icons close">close</i></div>
to be:
<div class="chip" data-object-id="123">Apple<i class="material-icons close">close</i></div>
The idea is to add some extra info to tags array like that:
[...]
10: [{tag: 'one', data: { object-id: 123} }, {tag: 'two', data: {object-id: 234}}],
[...]
and then add this to chips > div.chip while generating the entries...
Since I got stuck on the first problem I did not get to this yet but just wanted to ask - the only way to implement that would be to extend the material_chip by myself?
Thanks for any tips.

Displaying 3 dimensional arrays in correct order PHP

So I have dynamically created form with Jquery and after sending this form. I want to retrive it in correct order which I got myself confused in doing.
The problem is when you add multiple spells to a champion let's say 3 and delete middle one it will description and 'change' won't show up here is the code responsible for retriving variables from those arrays:
foreach($_POST['champion'] as $champion){
if(empty($_POST['GeneralChangeDescription'][$ChampionNumber])){
$NumberOfStats[$ChampionNumber+1] =0;}
if(!empty($_POST['GeneralChangeDescription'][$ChampionNumber])){
foreach($_POST['GeneralChangeDescription'][$ChampionNumber] as $indexGeneral=>$GeneralChangeDescription){
$GeneralChangeDescriptions[$ChampionNumber+1][$indexGeneral+1] = $GeneralChangeDescription;
$GeneralChange[$ChampionNumber+1][$indexGeneral+1]=$_POST['GeneralChange'][$ChampionNumber][$indexGeneral];
$NumberOfStats[$ChampionNumber+1] = count($_POST['GeneralChangeDescription'][$ChampionNumber]);
}
}
if(!empty($_POST['change'][$ChampionNumber])){
foreach($_POST['change'][$ChampionNumber] as $indexSpell=>$change){
$SpellIcon[$ChampionNumber+1][$indexSpell+1] = $change;
$SpellTitle[$ChampionNumber+1][$indexSpell+1] = $_POST['championSpell'][$ChampionNumber][$indexSpell];
$NumberOfSpellsChampion[$ChampionNumber+1] =count($_POST['change'][$ChampionNumber]);
foreach($_POST['SpellDescription'][$ChampionNumber][$indexSpell] as $indexChange=>$SpellDescription){
echo $ChampionNumber;
$ChampionNumberSpellNumber[$ChampionNumber+1][$indexSpell+1] =count($_POST['SpellDescription'][$ChampionNumber][$indexSpell]);
$SpellChange[$ChampionNumber+1][$indexSpell+1][$indexChange+1]=$_POST['SpellChange'][$ChampionNumber][$indexSpell][$indexChange];
$SpellDescriptions[$ChampionNumber+1][$indexSpell+1][$indexChange+1]=$SpellDescription;
}
}
}
$ChampionNumber++;
$championArray[$ChampionNumber] = $champion;
}
Here is JSfiddle with dynamic form http://jsfiddle.net/e2fk9793/
Here is an explanation how those arrays work http://imgur.com/DUsBgGz
The third foreach array is the one that is not working (Notice: Undefined offset: 2)

How do I bind a javascript array to HTML while allowing adding and deleting from the array?

So the question is how can I keep the HTML elements in sync while I add and delete from an existing array.
If I have an array of javascript objects say element 1 is:
{
"firstName": "John",
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
....
]
.... ETC ETC COMPLICATED ....
}
Then my initial html might be generated by using the array index for each object of initial size 3 elements [ {}, {}, {} ]:
<div id="arrayPos-0">
<div>John</div>
<input>PROCESS<input>
<input>DELETE<input>
</div>
<div id="arrayPos-1">
<div>Sam</div>
<input>PROCESS<input>
<input>DELETE<input>
</div>
<div id="arrayPos-2">
<div>Timmy</div>
<input>PROCESS<input>
<input>DELETE<input>
</div>
If I add to the Javascript Array, and I expect to increment it and become [ {}, {}, {}, {} ]:
<div id="arrayPos-0">
...
</div>
<div id="arrayPos-1">
...
</div>
<div id="arrayPos-2">
...
</div>
<div id="arrayPos-3">
<div>Simone</div>
<input>PROCESS<input>
<input>DELETE<input>
</div>
However as soon as I delete from the javascript array say index 2 (arrayPos-2) I get the following HTML:
<div id="arrayPos-0">
...
</div>
<div id="arrayPos-1">
...
</div>
<div id="arrayPos-3">
...
</div>
This is all messed up and I'm unable to match "arrayPos-3" to now index 2. In addition when I add a new javascript object which is index 3:
<div id="arrayPos-0">
...
</div>
<div id="arrayPos-1">
...
</div>
<div id="arrayPos-3">
...
</div>
<div id="arrayPos-3">
...
</div>
I am not able to use AngularJS and hence ng-repeat can't be used due to support needed on older browsers. It would be pretty simple to use an observer to bind the javascript objects directly to the html markup.
I can only use jQuery and regular javascript.
But surely this can be solved in a simpler manner? Do I even need to bind by using IDs? Do I need to autogenerate GUIDs for IDs and use a dictionary to match ID with Javascript object index (I hope not)?
The simplest way is to have a generator function which will regenerate the HTML after any changes have been made (adding/deleting/editing).
That way you won't have any confusions with IDs and will always have your current JS object represented in the DOM. That's quite the "Angularish" way to do it - building the DOM from the JS data without really caring what's in the DOM.
Here's a super-simple example to see what I meant:
var data = [
{name: 'John'},
{name: 'Shomz'},
{name: 'Jimmy'}
]
var c = document.getElementById('c');
function build() {
var output = "";
for(var i = 0; i < data.length; i++) {
output += '<div id="arrayPos-' + i + '"><button onclick="del(' + i + ')">Del</button><button onclick="edit(' + i + ')">Edit</button>' + data[i].name + '</div>';
}
c.innerHTML = output;
}
function del(id) {
data.splice(id ,1);
document.getElementById('arrayPos-' + id).className = 'del';
setTimeout(build, 200);
}
function add() {
data.push({name: prompt('Enter name')});
build();
}
function edit(id) {
data[id].name = prompt('Enter name', data[id].name);
build();
}
build();
#arrayPos-0 {color: green}
button {margin: 4px}
#c div {opacity: 1; transition: all 0.2s linear}
#c div:hover {background: #eee}
#c div.del {opacity: 0}
<div id="c"></div>
<br>
<button onclick="add()">Add</button>
You could use JQuery's .attr() method to dynamically set the id property when looping through the objects in the array - if that's the approach you want to take. You could also look at dynamically setting the inner html using JQuery's .html() to set the contents of the divs. If you review traversing the DOM using JQuery it will help with the latter approach. Hope that helps -
If you want to keep it this way and keep them in order then you caan create a function that:
1.deletes the current divs
2. Re generates the divs in the new order with the corresponding new id's
And just have this function fire every time you add or delete something from the array. It's relatively simple and wont take all that long for javascript to do.
Or:
Create a for loop that loops through the array and identifies which position(number of loops) the object with "x" name is in the array and assigns it to the div.
You can use the jQuery .index() method to get the index of the element relative to its siblings. With that, you can match it to the index in the array. No need for adding id's to the elements.
Let's say you already have an index and you want to access the dom element. You can do this:
$('.class-name').get(index).remove();
Or if you're responding to some event and you're not sure which element in the array it corresponds to, you can do this:
$('.class-name').click(function(e){
var index = $('.class-name').index(e.target);
});
In my example I'm using a click event, but it can be any other way.
In-case, if you can modify your existing js object, I suggest to use id for both html element and the js object.
Probably you could go for something:
var id = Math.random().toString(16).slice(-6); // for random id, aplha-numeric
Your html:
<div id="23xc45">
...
</div>
<div id="cd567u">
...
</div>
Your js object:
[{
"id" : "23xc45",
"firstName": "John",
...
},{
"id" : "cd567u",
"firstName": "Sam",
...
}]

KnockoutJS showing a sorted list by item category

I just started learning knockout this week and everything has gone well except for this one issue.
I have a list of items that I sort multiple ways but one of the ways I want to sort needs to have a different display than the standard list. As an example lets say I have this code
var BetterListModel = function () {
var self = this;
food = [
{
"name":"Apple",
"quantity":"3",
"category":"Fruit",
"cost":"$1",
},{
"name":"Ice Cream",
"quantity":"1",
"category":"Dairy",
"cost":"$6",
},{
"name":"Pear",
"quantity":"2",
"category":"Fruit",
"cost":"$2",
},{
"name":"Beef",
"quantity":"1",
"category":"Meat",
"cost":"$3",
},{
"name":"Milk",
"quantity":"5",
"category":"Dairy",
"cost":"$4",
}];
self.allItems = ko.observableArray(food); // Initial items
// Initial sort
self.sortMe = ko.observable("name");
ko.utils.compareItems = function (l, r) {
if (self.sortMe() =="cost"){
return l.cost > r.cost ? 1 : -1
} else if (self.sortMe() =="category"){
return l.category > r.category ? 1 : -1
} else if (self.sortMe() =="quantity"){
return l.quantity > r.quantity ? 1 : -1
}else {
return l.name > r.name ? 1 : -1
}
};
};
ko.applyBindings(new BetterListModel());
and the HTML
<p>Your values:</p>
<ul class="deckContents" data-bind="foreach:allItems().sort(ko.utils.compareItems)">
<li><div style="width:100%"><div class="left" style="width:30px" data-bind="text:quantity"></div><div class="left fixedWidth" data-bind="text:name"></div> <div class="left fixedWidth" data-bind="text:cost"></div> <div class="left fixedWidth" data-bind="text:category"></div><div style="clear:both"></div></div></li>
</ul>
<select data-bind="value:sortMe">
<option selected="selected" value="name">Name</option>
<option value="cost">Cost</option>
<option value="category">Category</option>
<option value="quantity">Quantity</option>
</select>
</div>
So I can sort these just fine by any field I might sort them by name and it will display something like this
3 Apple $1 Fruit
1 Beef $3 Meat
1 Ice Cream $6 Dairy
5 Milk $4 Dairy
2 Pear $2 Fruit
Here is a fiddle of what I have so far http://jsfiddle.net/Darksbane/X7KvB/
This display is fine for all the sorts except the category sort. What I want is when I sort them by category to display it like this
Fruit
3 Apple $1 Fruit
2 Pear $2 Fruit
Meat
1 Beef $3 Meat
Dairy
1 Ice Cream $6 Dairy
5 Milk $4 Dairy
Does anyone have any idea how I might be able to display this so differently for that one sort?
Your view shouldn't contain logic beyond that necessary to render it. Thus, your foreach binding
data-bind="foreach:allItems().sort(ko.utils.compareItems)" should become a computed observable.
You should move the <option> data into your model and take advantage of the options data-bind.
To address the actual question, you'll take advantage of template binding and containerless if binding.
The template binding will allow you to change the look/feel of the view based on the selected sort type. So 2 templates are available, the default-template which handles the regular display and the category-template specifically for category based rendering.
<script type="text/html" id="category-template">
<ul class="deckContents" data-bind="foreach:sortedItems">
<li>
<!-- ko if: $root.outputCategory($index()) -->
<div data-bind="text:category"></div>
<!-- /ko -->
<span class="indented" data-bind="text:name"></span>
<span class="indented" data-bind="text:cost"></span>
<span class="indented" data-bind="text:category"></span>
</li>
</ul>
The html usage: <div data-bind='template: { name: currentTemplate, data: $data}'></div> where currentTemplate is an computed observable that returns the template id based on sort type.
In some way or another you must assign priority to the categories. I have done this by declaring var categoryPriority = ["Fruit", "Meat", "Dairy"].
Have a look at my fiddle. I didn't address the fixedWidth used by the default-template so you'll need to handle the CSS styling to line it up the way you want.
Edit: Is there a way to dynamically add an item to the list and have it show up in the sorted list automatically?
Pushing a new item: When you want knockout to "notify" other elements then you don't want to read the observableArray by using allItems() before performing the push. Instead, you'll push into the observableArray using allItems.push which in-turn will cause knockout to trigger computed observables (that depend on this observable) to evaluate, subscriptions to execute, DOM elements to update ... etc.
Computed Dependencies: In order for a computed to "depend" on another observable it has to be read inside of the provided evaluator function. Since, sortedItems only reads sortType that is the only "trigger" for re-evaluation. Thus, changing the allItems.sort to allItems().sort causes sortedItems to evaluate whenever changes are made to allItems.
See How Dependency Tracking Works
By using sort method we can sort the list by ascending order.
I am taking your example and explain this.
food = [{
"name":"Apple",
"quantity":"3",
"category":"Fruit",
"cost":"$1",
},{
"name":"Ice Cream",
"quantity":"1",
"category":"Dairy",
"cost":"$6",
},{
"name":"Pear",
"quantity":"2",
"category":"Fruit",
"cost":"$2",
},{
"name":"Beef",
"quantity":"1",
"category":"Meat",
"cost":"$3",
},{
"name":"Milk",
"quantity":"5",
"category":"Dairy",
"cost":"$4",
}];
self.allItems = ko.observableArray(food);
Here you have an array. By using below code to sort this list
self.allItems.sort(function (left, right) {
return left.name() == right.name() ? 0 : (left.name() < right.name() ? -1 : 1)
});
like wise if u want to sort by cost ..is the same..Replace name to cost..u will get it.

Loading specific div based on the results of multiple dropdowns

EDIT: Would the approach be much easier if the Javascript listed was removed completely, and the dropdown menus restyled as <div>'s within <li>'s, and the final div was generated by a Javascript onclick event? e.g.
<a id="click_link">click me</a>
$("#click_link").click(function(){
$('#div').load('http://www.link.com/');
});
Either way, the problem at hand...
My decision to use an elegant-looking javascript solution is highlighting my massive inexperience when it comes to javascript! The problem is, on the face of it, simple...
Once an option has been chosen on each of the dropdown menus, I need a final div to load so that a specific button can be shown (a link to buy the item with the specified options, e.g. choosing Necklace D, with Stone Option B, and Delivery Option A = loading div with 'Buy' Button #17)
The dropdowns are divs that are filled and styled through the Javascript (as opposed to using the simpler <form> and <input> method), giving the flexibility to add two lines of differently styled text for each option etc. - This is where I step into the realm of the unknown and my inexperience shines through.
The isolated section is viewable in its entirity here
Ok, to the code.
Here's the Javascript:
function createByJson() {
var pearlData = [
{description:'Choose your pearl...', value:'Pearls', text:'Pearls'},
{description:'Beautiful black stone', value:'Black Pearl', text:'Black Pearl'},
{description:'Classic white stone', value:'White Pearl', text:'White Pearl'}
];
$("#DropItPearls").msDropDown({byJson:{data:pearlData, name:'pearls', width: 200}}).data("dd");
var blodeuweddData = [
{description:'Choose your item...', value:'Blodeuwedd', text:'the Blodeuwedd Collection'},
{description:'A striking statement', value:'BlodeuweddCelticStatement', text:'Celtic Statement Piece'},
{description:'Gold laced flower and pearl', value:'BlodeuweddBracelet', text:'Bracelet'},
];
$("#DropItBlodeuwedd").msDropDown({byJson:{data:blodeuweddData, name:'blodeuwedd', width: 250}})
.msDropDown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var deliveryData = [
{description:'Choose your method...', value:'Delivery', text:'Delivery Options'},
{description:'4-6 weeks delivery', value:'Four Weeks', text:'Made To Order'},
{description:'(unavailable on this item)', value:'Rush', text:'Express Delivery', disabled:true}
];
$("#DropItDelivery").msDropDown({byJson:{data:deliveryData, name:'delivery', width: 200, selectedIndex: 1}}).data("dd");
paymentData = [
{ description:'How would you like to pay?', value:'Payment', text:'Payment Method'},
{image:'images/msdropdown/icons/Visa-56.png', description:'Secure online payment', value:'Visa', text:'Visa'},
{image:'images/msdropdown/icons/Paypal-56.png', description:'Secure online payment', value:'Paypal', text:'Paypal'},
{image:'images/msdropdown/icons/EmailPay-56.png', description:'Order by email', value:'Email Payment', text:'Send Your Details'},
{image:'images/msdropdown/icons/Mastercard-56.png', description:'(coming soon)', value:'Mastercard', text:'Mastercard', disabled:true},
{image:'images/msdropdown/icons/Collect-56.png', description:'(coming soon)', value:'Collection', text:'Order and Collect', disabled:true},
{image:'images/msdropdown/icons/Email-56.png', description:'email Menna', value:'Other Method', text:'Alternatives'}
];
$("#DropItPayments").msDropDown({byJson:{data:paymentData, name:'payments', width: 250}}).data("dd");
}
$(document).ready(function(e) {
//no use
try {
var pages = $("#pages").msDropdown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var pagename = document.location.pathname.toString();
pagename = pagename.split("/");
pages.setIndexByValue(pagename[pagename.length-1]);
$("#ver").html(msBeautify.version.msDropdown);
} catch(e) {
//console.log(e);
}
$("#ver").html(msBeautify.version.msDropdown);
//convert
$("select").msDropdown();
createByJson();
$("#tech").data("dd");
});
function showValue(h) {
console.log(h.name, h.value);
}
$("#tech").change(function() {
console.log("by jquery: ", this.value);
})
//
And the html:
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Item</p></div>
<div id="DropItBlodeuwedd"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Precious Stones</p></div>
<div id="DropItPearls"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Payment</p></div>
<div id="DropItPayments"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Delivery</p></div>
<div id="DropItDelivery"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Buy Now!</p></div>
<div id="DropItBuy"></div>
</div>
Again, working version viewable here
Many thanks in advance!
What I think you want is for your Buy button to dynamically read what the dropdowns currently say and build a link for redirection based on that, rather than trying to update the Buy button every time a dropdown changes.
From your code I can't see what the form of the final URL is supposed to be. For example, to get the current value of the delivery option, you can check $('#DropItDelivery :selected').text() which will be something like "Made To Order".
Your Buy Now! could be a button with a click event that reads these values and constructs the URL with basic string concatenation, e.g.:
window.location = "buynow.html?delivery=" + $('#DropItDelivery :selected').text() +
"&payment=" + $('#DropItPayments :selected').text()
// etc.
Of course you'd have to handle these options on the server.
In case you want to redirect to the payment page of the processor, you can just branch based on the payment method and give them the URL you want based on that.
var pm = $('#DropItPayments :selected').text();
if (pm == "Visa")
{
// Visa payment URL construction
}
else if (pm == "Send Your Details")
{
// Send your details URL construction
}
// etc.

Categories

Resources