I have hundreds, maybe thousands of charts for stores. The only difference is the name of the store and the product. The HTML code is dynamically generated once/day. Even minified, it takes forever to load (at least it feels like an eternity). If I use Firebug, then loading the file does take a very, very long time to load.
The stores and products are created from a table that's generated each morning. Since each table has a title (e.g., "JohnsMarket"), the ids cannot be reduced to numbers (e.g., 'store1', 'store2').
All of the other SO solutions to repetitive code use numbered ids.
For each store/product, I have to repeat the following 3 snippets.
<div id="JohnsMarket_Soup" class="frmStep">
<div id="JohnsMarket_soup_chart" ></div>
<div class="layout">
<div class="layout_slider-settings">
<div class="loading" id="JohnsMarket_soup_loading"></div>
</div>
<div class="layout_slider"><input id="JohnsMarket_soup_slider" name="area" value="1" ></div>
<div class="layout_slider-settings"> </div>
</div>
</div>
if ( ui.panel.id==='JohnsMarket' )
{
if( typeof JohnsMarket_soup_chart_data === 'undefined' )
{
$('.loading_graph_msg').show();
window.setTimeout(function() { JohnsMarket_soup_data=checkData( JohnsMarket_soup_data,'JohnsMarket' );
JohnsMarket_soup_chart_data = createChart(JohnsMarket_soup_data, 'JohnsMarket_soup_chart', 'JohnsMarket Soup', 50, 7, -1); },50 );
$('.loading_graph_msg').hide('fast');
}
}
});
jQuery('#JohnsMarket_soup_slider').slider({}
$('#JohnsMarket_soup_loading').show();
var x = this.getValue();
window.setTimeout(function() {
JohnsMarket_soup_chart_data.replot();
JohnsMarket_soup_chart_data.destroy();
JohnsMarket_soup_chart_data = createChart(JohnsMarket_soup_data, 'JohnsMarket_soup_chart_data', 'JohnsMarket Soup', 5, x*7, -1);
},20 );
}
});
I can't say I fully understand what your whole problem statement looks like, but you can drastically compress all your code into one function that is used over and over again. This will, at least trim the size of the code down. Since you only showed one example of the data, I can't be sure what exactly is common from one data set to the next, but I made an assumption in order to show you how it can all be procedurized. You could collapse all the code to this:
function checkItem(idStr) {
if ( ui.panel.id == idStr) {
// generate derived names
var soupChartDataName = idStr + "_soup_chart_data";
var soupDataName = idStr + "_soup_data";
var soupChartData = idStr + "_soup_chart_data";
var soupChart = idStr + "_soup_chart";
var soup = idStr + " Soup";
var soupSlider = idStr + "_soup_slider";
var soupLoading = idStr + "_soup_loading";
if (typeof window[soupChartDataName] === 'undefined') {
$('.loading_graph_msg').show();
window.setTimeout(function() {
window[soupDataName] = checkData(window[soupDataName], idStr );
window[soupChartData] = createChart(window[soupChartData], soupChart, soup, 50, 7, -1);
}, 50);
$('.loading_graph_msg').hide('fast');
}
$("#" + soupSlider).slider({});
$("#" + soupLoading).show();
var x = this.getValue();
window.setTimeout(function() {
window[soupChartDataName].replot();
window[soupChartDataName].destroy();
window[soupChartDataName] = createChart(soupDataName, soupChartData, soup, 5, x*7, -1);
}, 20);
}
}
checkItem("JohnsMarket");
Then, for all the other items just call checkItem() with a different idString and no additional code. If I didn't guess the commonality among them quite correctly, you should be able to get the idea for how you can generate all the names being used from one or two common roots. For example, if "soup" isn't common among all the derived names, then maybe you need to pas that root into checkItem too so it can vary from one name to the next. If this were my code, I wouldn't be using so many global variables and I'd hang them off some object in my page, but that's your choice.
Note - for global variables, we access them off the windowobject so we can use the derived variable names as indexes.
And, you could create the HTML from a string template like this:
function createItem(idStr) {
var template = '<div id="xxxx_soup_chart" ></div><div class="layout"><div class="layout_slider-settings"><div class="loading" id="xxxx_soup_loading"></div></div><div class="layout_slider"><input id="xxxx_soup_slider" name="area" value="1" ></div><div class="layout_slider-settings"> </div></div>';
var o = document.createElement("div");
o.id = idStr + "_Soup";
o.className = "frmStep";
o.innerHTML = template.replace(/xxxx/g, idStr);
document.body.append(o); // change this to append the item wherever it's supposed to go
}
createItem("JohnsMarket");
Any of that data seems like it could be stored in a hash keyed on the store name, including the chart itself; the rest is just string concatenation. But I agree, I'd try to move some of that onto the server side, even if it's just to retrieve the data used to create the charts.
Related
I am looking at getting my "NodeNumber" variable into the parameter of my loadSynchPage function.
my code looks at the name of the page (e.g. page.htm from http://www.example.com/tree/page.htm) and after a series of lines gives a "NodeNumber". I would like the page to work out the NodeNumber and put it into the function when the page loads.
So I'd guess it would look something like this:
<BODY onload="loadSynchPage(NodeNumber);">
I have got my code to output the NodeNumber using a button with onlick.
I have also got the loadSynchPage(linkID) function, to work using a button with onclick where I enter the number myself. button and have also got a variable which I predefine to work; but I can't get it to read my "NodeNumber" and I have spent 2 days trying to figure this out.
Here is my code:
<script>
function variables() {
//Getting the page's name from the URL
PageName = location.pathname.split("/").slice(-1);
document.getElementById("PageName").innerHTML = PageName;
//Converting the page name into a string
PageNameString = String(PageName);
document.getElementById("PageNameString").innerHTML = PageNameString;
//Converting the string of the page name, into it's length
PageLength = PageNameString.length;
document.getElementById("PageLength").innerHTML = PageLength;
//Setting the DemoFramesetNodes.js as a variable (Copy and Paste in - needs to have no " and be on one line)
TextFromDemoFramesetNodesJS = "foldersTree = gFld(<i>Page 1 Title</i>, page1.htm)//000foldersTree.treeID = Framesetaux1 = insFld(foldersTree, gFld(Page 2 Title, page 2.htm)) //001insDoc(aux1, gLnk(R, Page 3 Title, Page3.htm)) //002aux1 = insFld(foldersTree, gFld(Page 4 Title, Page4.htm)) //003insDoc(aux1, gLnk(R, Page 5 Title, Page5.htm)) //004";
//Need to set the difference BETWEEN the "m" in htm and the start of the number
//e.g. page2.htm)) //001
CharacterDifference = "5";
document.getElementById("CharacterDifference").innerHTML = CharacterDifference;
//Need to find the location of the page's name from the DemoFramsetNodes
PageNameLocation = TextFromDemoFramesetNodesJS.lastIndexOf(PageNameString);
document.getElementById("PageNameLocation").innerHTML = PageNameLocation;
//Need to convert all variables to be added into numbers
PageNameLocationNumber = Number(PageNameLocation);
PageLengthNumber = Number(PageLength);
CharacterDifferenceNumber = Number(CharacterDifference);
document.getElementById("CharacterDifferenceNumber").innerHTML = CharacterDifferenceNumber;
//Add the variables to give us the start location of the Node Number
NodeNumberStart = PageNameLocationNumber + PageLengthNumber + CharacterDifferenceNumber;
document.getElementById("NodeNumberStart").innerHTML = NodeNumberStart;
//Add the variables to give us the end location of the Node Number
NodeNumberEnd = PageNameLocationNumber + PageLengthNumber + CharacterDifferenceNumber + 3;
document.getElementById("NodeNumberEnd").innerHTML = NodeNumberEnd;
//this gives us the Node Number that we want
NodeNumber = TextFromDemoFramesetNodesJS.substring(NodeNumberStart, NodeNumberEnd);
document.getElementById("NodeNumber").innerHTML = NodeNumber;
}
var avar=Number(window.NodeNumber);
//window.NodeNumber;
function loadSynchPage(linkID) {
var folderObj;
docObj = parent.treeframe.findObj(linkID);
docObj.forceOpeningOfAncestorFolders();
parent.treeframe.HightlightNode(linkID,docObj.link,'basefrm');
if (typeof parent.treeframe.document.body != "undefined") // To handle scrolling not working with NS4
parent.treeframe.document.body.scrollTop=docObj.navObj.offsetTop
}
//myvar=Number(NodeNumber);
// function loadnode(){
// document.getElementById('loadSynchPage').onclick=function(){loadSynchPage(myvar);};
// var myvar=15;
//function init(){
// document.getElementById('EditBanner').onclick=function(){EditBanner(myvar);};
// }
</script>
</head>
<BODY>
<button onclick="javascript:settingvariables();">Page Highlight1</button>
<button onclick="javascript:loadSynchPage(avar);">Page Highlight2</button>
Page Name: <p id="PageName"></p>
Page Name String: <p id="PageNameString"></p>
Page Length: <p id="PageLength"></p>
Character Difference: <p id="CharacterDifference"></p>
Page Name Location: <p id="PageNameLocation"></p>
Character Difference Number:<p id="CharacterDifferenceNumber"></p>
Node Number Start: <p id="NodeNumberStart"></p>
Node Number End: <p id="NodeNumberEnd"></p>
Node Number: <p id="NodeNumber"></p>
As you can see from the "//" near the bottom, I have already tried some different ways from other questions on stackoverflow - I am sure I have tried everything and am missing something really simple :/
I have also taken out the "var" before setting things like PageName and NodeNumber after w3 said that this would then allow them to become global variables?
To give more information:
The code is based on TreeView - it has a tree with nodes on the left and you click on the nodes to open the page in the right hand frame. However clicking on a link from a page in the right page to a different page doesn't update the highlighting on the node and that is why I am trying to do this (onload).
EDIT: If possible I would also like to get the code into an external .js and reference it as I have multiple pages (I tried this for a few hours last week with different, working code, but also couldn't get this to work either).
All you need is:
window.onload = function() {
loadSynchPage(NodeNumber);
};
Or the onload in body tag, the problem you have is that it doesn't work in the sandbox in this snippet:
Uncaught SecurityError: Sandbox access violation: Blocked a frame at
"http://stacksnippets.net" from accessing a frame at
"http://stackoverflow.com". The frame requesting access is sandboxed
and lacks the "allow-same-origin" flag.
function variables() {
//Getting the page's name from the URL
PageName = location.pathname.split("/").slice(-1);
document.getElementById("PageName").innerHTML = PageName;
//Converting the page name into a string
PageNameString = String(PageName);
document.getElementById("PageNameString").innerHTML = PageNameString;
//Converting the string of the page name, into it's length
PageLength = PageNameString.length;
document.getElementById("PageLength").innerHTML = PageLength;
//Setting the DemoFramesetNodes.js as a variable (Copy and Paste in - needs to have no " and be on one line)
TextFromDemoFramesetNodesJS = "foldersTree = gFld(<i>Page 1 Title</i>, page1.htm)//000foldersTree.treeID = Framesetaux1 = insFld(foldersTree, gFld(Page 2 Title, page 2.htm)) //001insDoc(aux1, gLnk(R, Page 3 Title, Page3.htm)) //002aux1 = insFld(foldersTree, gFld(Page 4 Title, Page4.htm)) //003insDoc(aux1, gLnk(R, Page 5 Title, Page5.htm)) //004";
//Need to set the difference BETWEEN the "m" in htm and the start of the number
//e.g. page2.htm)) //001
CharacterDifference = "5";
document.getElementById("CharacterDifference").innerHTML = CharacterDifference;
//Need to find the location of the page's name from the DemoFramsetNodes
PageNameLocation = TextFromDemoFramesetNodesJS.lastIndexOf(PageNameString);
document.getElementById("PageNameLocation").innerHTML = PageNameLocation;
//Need to convert all variables to be added into numbers
PageNameLocationNumber = Number(PageNameLocation);
PageLengthNumber = Number(PageLength);
CharacterDifferenceNumber = Number(CharacterDifference);
document.getElementById("CharacterDifferenceNumber").innerHTML = CharacterDifferenceNumber;
//Add the variables to give us the start location of the Node Number
NodeNumberStart = PageNameLocationNumber + PageLengthNumber + CharacterDifferenceNumber;
document.getElementById("NodeNumberStart").innerHTML = NodeNumberStart;
//Add the variables to give us the end location of the Node Number
NodeNumberEnd = PageNameLocationNumber + PageLengthNumber + CharacterDifferenceNumber + 3;
document.getElementById("NodeNumberEnd").innerHTML = NodeNumberEnd;
//this gives us the Node Number that we want
NodeNumber = TextFromDemoFramesetNodesJS.substring(NodeNumberStart, NodeNumberEnd);
document.getElementById("NodeNumber").innerHTML = NodeNumber;
}
var avar=Number(window.NodeNumber);
//window.NodeNumber;
function loadSynchPage(linkID) {
alert("loadSynchPage called");
var folderObj;
docObj = parent.treeframe.findObj(linkID);
docObj.forceOpeningOfAncestorFolders();
parent.treeframe.HightlightNode(linkID,docObj.link,'basefrm');
if (typeof parent.treeframe.document.body != "undefined") // To handle scrolling not working with NS4
parent.treeframe.document.body.scrollTop=docObj.navObj.offsetTop
}
loadSynchPage(NodeNumber);
//myvar=Number(NodeNumber);
// function loadnode(){
// document.getElementById('loadSynchPage').onclick=function(){loadSynchPage(myvar);};
// var myvar=15;
//function init(){
// document.getElementById('EditBanner').onclick=function(){EditBanner(myvar);};
// }
<button onclick="javascript:settingvariables();">Page Highlight1</button>
<button onclick="javascript:loadSynchPage(avar);">Page Highlight2</button>
Page Name: <p id="PageName"></p>
Page Name String: <p id="PageNameString"></p>
Page Length: <p id="PageLength"></p>
Character Difference: <p id="CharacterDifference"></p>
Page Name Location: <p id="PageNameLocation"></p>
Character Difference Number:<p id="CharacterDifferenceNumber"></p>
Node Number Start: <p id="NodeNumberStart"></p>
Node Number End: <p id="NodeNumberEnd"></p>
Node Number: <p id="NodeNumber"></p>
2nd question: Save all the script (not including the <script> tags) to myjs.js, put it to the same directory next to page.html, and remove the whole <script> block from html, and put this instead of it:
<script src="myjs.js"></script>
I am trying to generate multiple unique sets of divs within the existing div with an id of row. I would like the output to be this:
<div id="row">
<div>fluffy</div>
<div>soft</div>
<div>green</div>
<div>mittens</div>
<div>coarse</div>
<div>fire</div>
<div>whiskers</div>
<div>none</div>
<div>grey</div>
</div>
However my output is currently this:
<div id="row">
<div>fluffy, soft, green</div>
<div>mittens, coarse, fire</div>
<div>whiskers, none, grey</div>
</div>
I am using the following jquery:
function cats(catName, catFur, catEyes) {
$("#row").html('<div>' + catName + '</div>' + '<div>' + catFur + '</div>' + '<div>' + catEyes + '</div>');
}
$cat1 = ["fluffy", "soft", "green"];
$cat2 = ["mittens", "coarse", "fire"];
$cat3 = ["whiskers", "none", "grey"];
jQuery(document).ready(function() {
function catGenerator() {
$catVars = [$cat1, $cat2, $cat3];
cats($catVars[0], $catVars[1], $catVars[2] );
}
catGenerator();
});
My overall goal is to be able to automate the number div sets to be generated, for example, there are 3 div sets to be generated here, so instead of listing every cat array under the catGenerator function, it would automatically know how many sets to generate. I believe this would be implemented with a for loop, but I haven't been able to figure it out.
I feel overall I am missing a bigger concept here, and I would love to know if anyone has a better way of going about this overall problem, a more best practices approach. Thanks!
codepen:
http://codepen.io/anon/pen/IfKek
Try this.
function catGenerator() {
$catVars = [$cat1, $cat2, $cat3];
for (var i = 0; i < $catVars.length; i++) {
cats($catVars[i][0], $catVars[i][1], $catVars[i][2]);
}
}
Actually "$catVars" is a two dimensional array.
$catVars[0][0] = "fluffy";
$catVars[0][1] = "soft";
$catVars[0][2] = "green";
$catVars[1] = ["mittens", "coarse", "fire"]
$catVars[2] = ["whiskers", "none", "grey"];
I created a div and a button. when the button clicked, there will be a group of element(included 1 select box and 2 text inputs) inserted into the div. User can add as many group as they can, when they finished type in data of all the group they added, he can hit save button, which will take the value from each group one by one into the JSON object array. But I am stuck in the part how to get the value from each group, so please help, thank you.
The code for the div and the add group button function -- AddExtra() are listed below:
<div id="roomextra">
</div>
function AddExtra() {
$('#roomextra').append('<div class=extra>' +
'<select id="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" id="insetLength">' +
'Width(m): <input type="text" id="insetWidth">' +
'Height(m): <input type="text" id="insetHeight">' +
'</div>');
}
function GetInsetOffSetArray (callBack) {
var roomIFSDetail = [{
"IsInset": '' ,
"Length": '' ,
"Width": '' ,
"Height": ''
}];
//should get all the value from each group element and write into the array.
callBack(roomIFSDetail);
}
This should just about do it. However, if you're dynamically creating these groups, you'll need to use something other than id. You may want to add a class to them or a data-* attribute. I used a class, in this case. Add those classes to your controls so we know which is which.
var roomIFSDetail = [];
var obj;
// grab all of the divs (groups) and look for my controls in them
$(.extra).each(function(){
// create object out of select and inputs values
// the 'this' in the selector is the context. It basically says to use the object
// from the .each loop to search in.
obj = {
IsInset: $('.isInset', this).find(':selected').val() ,
Length: $('.insetLength', this).val() ,
Width: $('.insetWidth', this).val() ,
Height: $('.insetHeight', this).val()
};
// add object to array of objects
roomIFSDetail.push(obj);
});
you'd better not to use id attribute to identity the select and input, name attribute instead. for example
$('#roomextra').append('<div class=extra>' +
'<select name="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" name="insetLength">' +
'Width(m): <input type="text" name="insetWidth">' +
'Height(m): <input type="text" name="insetHeight">' +
'</div>');
}
and then, usr foreach to iterate
$(".extra").each(function() {
var $this = $(this);
var isInset = $this.find("select[name='isInset']").val();
var insetLength = $this.find("input[name='insetLength']").val();
// ... and go on
});
A common problem. A couple things:
You can't use IDs in the section you're going to be repeating, because IDs in the DOM are supposed to be unique.
I prefer to use markup where I'm writing a lot of it, and modify it in code rather than generate it there.
http://jsfiddle.net/b9chris/PZ8sf/
HTML:
<div id=form>
... non-repeating elements go here...
<div id=roomextra>
<div class=extra>
<select name=isInset>
<option>Inset</option>
<option>OffSet</option>
</select>
Length(m): <input id=insetLength>
Width(m): <input id=insetWidth>
Height(m): <input id=insetHeight>
</div>
</div>
</div>
JS:
(function() {
// Get the template
var container = $('#roomextra');
var T = $('div.extra', container);
$('#addGroup').click(function() {
container.append(T.clone());
});
$('#submit').click(function() {
var d = {};
// Fill d with data from the rest of the form
d.groups = $.map($('div.extra', container), function(tag) {
var g = {};
$.each(['isInset', 'insetLength', 'insetWidth', 'insetHeight'], function(i, name) {
g[name] = $('[name=' + name + ']', tag).val();
});
return g;
});
// Inspect the data to ensure it's what you wanted
debugger;
});
})();
So the template that keeps repeating is written in plain old HTML rather than a bunch of JS strings appended to each other. Using name attributes instead of ids keeps with the way these elements typically work without violating any DOM constraints.
You might notice I didn't quote my attributes, took the value attributes out of the options, and took the type attributes out of the inputs, to keep the code a bit DRYer. HTML5 specs don't require quoting your attributes, the option tag's value is whatever the text is if you don't specify a value attribute explicitly, and input tags default to type=text if none is specified, all of which adds up to a quicker read and slimmer HTML.
Use $(".extra").each(function() {
//Pull info out of ctrls here
});
That will iterate through all of your extra divs and allow you to add all values to an array.
HTML code:
<td>
<img src="../images/items/333.png"><br>
<b>Product One</b><br>
(2 in stock)<br>
<i>65 USD</i><br>
<form action="shopping.php?shop=1&item=333&price=65&buy=conf&type=" method="post">
<input name="" value="Buy this item" type="submit">
</form></td>
<td>
<img src="../images/items/444.png"><br>
<b>Product Two</b><br>
(4 in stock)<br>
<i>5 USD</i><br>
<form action="shopping.php?shop=1&item=444&price=5&buy=conf&type=" method="post">
<input name="" value="Buy" type="submit">
</form></td>
This is the html code on the page I'm working on, the html code cannot be altered.
There are several td tags on the page which contains the following information you see in the code above.
I would like to write a script that would do something like this:
if (document.body.innerHTML.indexOf("Product One") > -1) {
document.location = "shopping.php?shop=1&item="+itemID+"&price="+USD+"&buy=conf&type="
}
Search the body/td of the page for the "Product Name" specified in my script, and then if it's found, go to the url that contains variables that need to be extracted, itemID and USD.
itemID is extracted from the src of the image.png by taking the numbers. For example, the itemID of ../images/items/444.png is 444.
USD is extracted from the price defined between the italics tags. For example the extracted value for USD for<i>5 USD</i> would be 5.
Catch is
I would need a lot of if (document.body.innerHTML.indexOf("Name") > -1) {document.location = "shopping.php?shop=1&item="+itemID+"&price="+USD+"&buy=conf&type="} to cater to the large number of products I would specify. I might want to specify "Product One to Hundred" and "Sub-product A to Z"
Solution
Some ways I thought of to handle this (needs to be put into javascript code) is to:
Put list of products I will specify into an array (something like) var list = new Array ("Product One","Product Two","Sub-Product A"); and have a function check the page for the presence of any product from this array that displays on the page.
When the product is found, to get the itemID, isolate the numbers before .png and after /items/ from the image src of the product. And to get USD, get the value between the <i> </i> tags and only take the numerical values
To do this I think nextSibling or previousSibing can be used, but I'm not too sure about that.
Alternatively, to make it easier, there could be a function to immediately locate the form's action value and set the window.location since <form action="shopping.php?shop=1&item=444&price=5&buy=conf&type=" method="post">
I've seen this done before using XPath?
This is not difficult using jQuery -- especially if we extend it to search for case-insensitive, regular expressions.
The following script should work with the HTML structure from the question, if it is precisely accurate and not added by AJAX. Note the power that regular expressions give when targeting product descriptions.
You can see the underlying code at work at jsFiddle.
// ==UserScript==
// #name _Auto-follow targeted product links.
// #include http://YOUR_SERVER.COM/YOUR_PATH/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// ==/UserScript==
var targetList = [
"Shoes, pumps",
"Horse shoes",
"(?:Red|Pink|Burgundy)\\s+shoes?"
];
/*--- Extend jQuery with a case-insensitive version of contains().
Also allows regular expressions.
*/
jQuery.extend (
jQuery.expr[':'].containsCI = function (a, i, m) {
//--- The next is faster than jQuery(a).text()...
var sText = (a.textContent || a.innerText || "");
var zRegExp = new RegExp (m[3], 'i');
return zRegExp.test (sText);
}
);
$.each (targetList, function (index, value) {
var jqSelector = 'td > b:containsCI("' + value + '")';
var productFound = $(jqSelector);
if (productFound.length) {
var matchingForm = productFound.first ().nextAll ("form");
if (matchingForm.length) {
alert (productFound.text () );
document.location = matchingForm.attr ("action");
}
}
} );
Here is a solution that does not rely on external libraries like jQuery:
function findProducts(referenceList) {
var productsOnPage = {}, listMatches = {},
tds = document.getElementsByTagName("TD"),
bold, form, i, productName, productUrl;
// build an index of products on the page (HTML scraping)
for (i = 0; i < tds.length; i++) {
bold = tds[i].getElementsByTagName("B")[0];
form = tds[i].getElementsByTagName("FORM")[0];
if (bold && form) {
productName = bold.innerHTML.toLowerCase();
productUrl = form.action;
productsOnPage[productName] = productUrl;
}
}
// match reference list against all available products on the page
for (i = 0; i < referenceList.length; i++) {
productName = referenceList[i].toLowerCase();
productUrl = productsOnPage[productName];
listMatches[productName] = productUrl;
}
return listMatches;
}
Call it:
var availableProducts = findProducts(["Product One","Product Two","Sub-Product A"]);
After that you'd have an object availableProducts that's looking like this:
{
"product one": "shopping.php?shop=1&item=333&price=65&buy=conf&type=",
"product two": "shopping.php?shop=1&item=444&price=5&buy=conf&type=",
"sub-product a": undefined
}
Note that all keys are lower case to make string comparisons consistent. To look up a product you would use
function navigateIfAvailable(productName) {
var url = availableProducts[productName.toLowerCase()];
if (url) document.location = url;
}
Now
navigateIfAvailable("Product Two");
would go somewhere. Or not.
I have taken a few different approaches at solving this problem, but have never really been happy with the inflexibility of my solutions. I am trying to map an object created in javascript to a container in my html document.
Let me give you an example. Say I have a Phone Number object in javascript:
var PhoneNumber = function(number, type) {
this.number = number;
this.type = type;
}
var myPhone = new PhoneNumber('5555555555', 'Home');
And within my html document, I have a series of DIV's which are basically empty containers waiting to be populated by the forementioned object and displayed to the user.
<div id="phoneNumberContainer">
<div class="row">
<div id="number"></div>
<div id="type"></div>
</div>
</div>
Now, I want to have a function within my javascript that will automatically take that phone number object and fill the phoneNumberContainer elements with its data. I have multiple types of objects with different parameters so it has to be generic.
Currently I find myself doing a lot of this:
function mapPhoneNumber() {
var numberContainer = document.getElementById('number');
numberContainer.innerHTML = myPhone.number;
var typeContainer = document.getElementById('type');
typeContainer.innerHTML = myPhone.type;
}
Which of course works just fine, but its calls for writing a lot of the same code over and over again for every single object I want to map. I wish there was a way to do it like this so I could just pass the object and container into a function and it automatically populate the DIV elements based on the object:
function mapPhoneNumber(phoneObject) {
var phoneContainerChildren = document.getElementById('phoneNumberContainer').childNodes;
for(x in phoneContainerChildren) {
phoneContainerChildren[x] = myPhone.number;
}
}
Of course this is not a working example as the order of the containers are not guaranteed to be the same as the phone number objects, and the children will have other types of nodes beside my divs etc etc. I could put the phone number parameter name in the actual ID of the DIV element and then match it against the object and fill the object when I have a match, but I hate to have to have such specific ID's on my DIV elements that if changed by another designer will completely destroy my javascript.
Does anyone have any suggestions on how I might implement something like this? I am not against jQuery as I do use a lot of it in my solutions.
I'm not 100 % sure if it's what you're looking for but I think this might be a possible solution:
var DOMable = {
writeToDOM : function( node ){
for( var i in this ) if( this.hasOwnProperty( i ) ){
node.find( "." + i ).text( this[i] );
}
}
};
var PhoneNumber = function(number, type) {
this.number = number;
this.type = type;
};
PhoneNumber.prototype = DOMable;
var myPhone = new PhoneNumber('5555555555', 'Home');
Then you can call:
myPhone.writeToDOM( jQuery( '#myPhone' ) );
for a HTML structure like:
<div id="myPhone">
<div class="number">
</div>
<div class="type">
</div>
</div>
I use something similar to this in many of my projects.
<div id="container">
<div class="row">
<div class="prop"></div>
<div class="propValue"></div>
</div>
</div>
function BindValues(container, prop, propValue)
{
container.find(".prop").val = prop;
container.find(".propValue").val = propValue;
return container;
}
You can add a method to your phone number object and use classes in your HTML instead of IDs so you can have more than one per page. Then, all your code is attached to the object and you don't have to retype it everytime you want it:
<div id="homePhone" class="phoneNumberContainer">
<div class="row">
<div class="number"></div>
<div class="type"></div>
</div>
</div>
var PhoneNumber = function(number, type) {
this.number = number;
this.type = type;
}
PhoneNumber.prototype.setHTML(dest) {
var item = $(dest);
item.find(".type").html(this.type);
item.find(".number").html(this.number);
}
var myPhone = new PhoneNumber('5555555555', 'Home');
myPhone.setHTML("#homePhone");
To do this any more automatically would require us knowing more about how phone numbers are created, how the HTML is created and how one can be automatically associated with the other? Do you have a list of PhoneNumber objects that you just want to create a bunch of corresponding HTML? Or does the HTML already exist and you want to fill it?