This is the script present in the html web-page.
jQuery(function($) {
new Shopify.OptionSelectors('productSelect', {
product: {
"id":626976579,
"title":"changedMacbook Air",
"handle":"macbook-air",
"description":"\u003cp\u003elightweight \u003c\/p\u003e\n\u003cp\u003eawesome performance\u003c\/p\u003e\n\u003cp\u003ewow display\u003c\/p\u003e\nHello World626976579[\"78000.00\"] [\"78000.00\"]\\n[\"78000.00\"] [\"78000.00\"]\u003cbr\u003e[\"78000.00\"]\u003cbr\u003e626976579\u003cbr\u003e626976579\u003cbr\u003e626976579",
"published_at":"2015-05-25T02:39:00-04:00",
"created_at":"2015-05-25T02:40:44-04:00",
"vendor":"Test_Store",
"type":"Computers",
"tags":[],
"price":7800000,
"price_min":7800000,
"price_max":7800000,
"available":true,
"price_varies":false,
"compare_at_price":null,
"compare_at_price_min":0,
"compare_at_price_max":0,
"compare_at_price_varies":false,
"variants":[{"id":1754837635,"title":"Default Title","options":["Default Title"],"option1":"Default Title","option2":null,"option3":null,"price":7800000,"weight":800,"compare_at_price":null,"inventory_quantity":-29,"inventory_management":null,"inventory_policy":"deny","available":true,"sku":"20","requires_shipping":true,"taxable":true,"barcode":"","featured_image":null}],"images":["\/\/cdn.shopify.com\/s\/files\/1\/0876\/1234\/products\/overview_wireless_hero_enhanced.png?v=1432536113"],"featured_image":"\/\/cdn.shopify.com\/s\/files\/1\/0876\/1234\/products\/overview_wireless_hero_enhanced.png?v=1432536113","options":["Title"],"content":"\u003cp\u003elightweight \u003c\/p\u003e\n\u003cp\u003eawesome performance\u003c\/p\u003e\n\u003cp\u003ewow display\u003c\/p\u003e\nHello World626976579[\"78000.00\"] [\"78000.00\"]\\n[\"78000.00\"] [\"78000.00\"]\u003cbr\u003e[\"78000.00\"]\u003cbr\u003e626976579\u003cbr\u003e626976579\u003cbr\u003e626976579"},
onVariantSelected: selectCallback,
enableHistoryState: true
});
How the value of "title" field be accessed, which here it is "changedMacbook Air" via my own JavaScript?
Thanks in advance.
I don't know if it will work but try
var myProduct = new Shopify.OptionSelectors('productSelect', {
....
})
then try
console.log(myProduct)
or you can try this:
$(document).ready(function(e){
console.log(document.title);
})
I think you might have to pass it through the callback.
$('#productSelect').on('change', function(e) {
var t = e.target || e.srcElement,
title = t.title.value;
selectCallback(title);
break;
}
}
});
//If you want to trigger it right away for some reason, just use this...
$("#productSelect").change();
Look for the code for option_selection.js in your products template or somewhere in your Snippets, Assets, or Templates. See this fiddle for an example of what the code looks like. option_selection.js
You might also want to check this link, it's setting up an onchange event on the product variants. I'm not sure what your ultimate goal is but I see you are working with product options and variants so it might be helpful.
You can modify option_selection.js if you need to, but more than likely you will just need some jquery in the document.ready.
Here is an example from option_selection.js that builds the names of each selector. Though you probably don't need to modify this, see link above.
Shopify.OptionSelectors.prototype.buildSelectors = function() {
for (var t = 0; t < this.product.optionNames().length; t++) {
var e = new Shopify.SingleOptionSelector(this, t, this.product.optionNames()[t], this.product.optionValues(t));
e.element.disabled = !1, this.selectors.push(e)
}
var o = this.selectorDivClass,
i = this.product.optionNames(),
r = Shopify.map(this.selectors, function(t) {
var e = document.createElement("div");
if (e.setAttribute("class", o), i.length > 1) {
var r = document.createElement("label");
r.htmlFor = t.element.id, r.innerHTML = t.name, e.appendChild(r)
}
return e.appendChild(t.element), e
});
return r
},
What I'm trying to do is create a dynamic wall of images.
What I'm doing is this:
Call an API to get some response. Create an array of objects based on response
Based on the array, make HTML elements for each object where there's an img in it too.
When all of these HTML elements are created, attach it to the DOM, and call a final function.
This is what I have so far (truncated to get the point across):
EDIT: code has changed a bit. scroll to bottom of question for link to current code.
// based on one post, construct the html and return it
function getOneHtml(post, w) {
console.log("getting one html");
var outerDiv = $("<div>", {class: "brick"});
outerDiv.width(w);
var img = $("<img />");
img.attr("src", post.img_src);
img.on('load', function() {
console.log("img loaded");
var ratio = this.width / w;
h = this.height / ratio;
$(this).css({'height': h});
// ...
// ...
// create the element
// an alternative I'm using for now is directly append
// the created HTML onto the page, but that results
// in a kinda messy interface.
return outerDiv[0].outerHTML;
});
}
// queries an api and then calls callback after everything is done
function requestData(subreddit, callback) {
// array of objects with link to image, post title, link to reddit
posts = [];
var w = $(window).innerWidth() / 3,
html = ''; // holds all of the inner HTML for all elements
$.get("url here", function(data) {
var arr = data.data.children;
arr.forEach(function(res_post) {
console.log("looping in requestData");
// prepare a post object
// ...
// ...
html += getOneHtml(post, w); // get the HTML for this post
});
// this should happen after everything else is done
console.log("calling callback");
callback(html);
});
}
// complete the DOM
function makeWall(html) {
console.log("making wall");
// do stuff
}
Now the trace of the program in console is this:
looping in requestData
getting one html
looping in requestData
getting one html
... // bunch of times
calling callback
making wall
(20) img loaded
So now the problem is that the HTML isn't prepared until each image is loaded, and so it doesn't actually get attached to the DOM.
How can I make sure that things happen in order in which I want them to? I tried refactoring code into more of an async style but that didn't work (not my strongest point).
I also tried looking at $.Deferred but I don't understand it, and how to integrate it into my code.
Any help is appreciated.
EDIT:
I think it might help to see what I'm doing: http://karan.github.io/griddit/
When you load, I want the images to load first, and then fade in. Currently, they show up, then hide and then fade in. Here's the source: https://github.com/karan/griddit/blob/gh-pages/js/main.js.
Also, if you scroll down one or two pages, then scroll back up, some images show up behind others.
You can use .done(), as explained in the jQuery API documentation on .done() specifically explains how to use .done() with $.get().
It's as simple as:
$.get( "test.php" ).done(function() {
alert( "$.get succeeded" );
});
Concretely...
As the API documentation link provided above indicates, you can daisy chain .done() calls together.
$.get(url,handler).done(function(){console.log('calling callback')},callback);
Note, Not certain about functionality of included plugins, i.e.g., #grid layout,
css, etc. image width, height and #grid layout not addressed, save for existing pieces re-composed in attempt at flow clarity.
Piece below solely to fulfill // Do something when all dynamically created images have loaded requirement. See console at jsfiddle
Note also, piece at jsfiddle format issue. In order to test piece, drew in 2 plugins from links at original post. Tried jsfiddle's TidyUp feature, which inserted linebreaks.
Piece may need some re-formatting; though current jsfiddle does provide callback functionality, as per original post. Again, see console. Thanks for sharing.
updated
$(function() {
// the name of the last added post
var last_added = '';
// to control the flow of loading during scroll var scrollLoad = true;
var q = 'cats';
var callbacks = $.Callbacks();
// callback,
// Do something when all dynamically created images have loaded
var callback = function (cb) {
return console.log( cb||$.now() )
};
callbacks.add(callback);
function getOneHtml(post, w, count){
var img = $("<img>", {
"src" : post.img_src,
"width" : w
});
img.on('load', function(e) {
var ratio = e.target.width / w;
h = e.target.height / ratio;
$(e.target).css('height',h)
});
var link = $("<a>", {
"href" : post.permalink,
"target" : "_blank",
"html" : img
});
var outerDiv = $("<div>", {
"class" : "brick",
"style" : "width:" + w
});
$.when($(outerDiv).appendTo("#grid"),
$(link),
count)
.then(function(div, _link, _count) {
// `image` `fadeIn`; adjustable
$(_link).appendTo($(div)).hide(0).fadeIn(2000);
return _count
})
.always(function(_count){
callbacks.fireWith(window, [_count + " images appended to grid at " + $.now()])
});
};
function requestData(subreddit,callback) {
//array of objects with link to image, post title,link to reddit
posts=[];
var w = $(window).innerWidth() / 3;
html = '';
$.ajax({
type : 'get',
url : "http://api.reddit.com/r/" + subreddit + "/hot.json?&after=" + last_added,
beforeSend : function () {
$("#searchterm").addClass("loadinggif");
},
complete : function () {
$("#searchterm").removeClass("loadinggif");
},
success : function (data) {
var arr = data.data.children;
var count = null;
arr.forEach(function(res_post) {
if(!res_post.data.is_self&&(/\.(gif|jpg|jpeg|tiff|png)$/i).test(res_post.data.url)) {
// `images` count
++count;
var post = {
'title' : res_post.data.title,
'img_src': res_post.data.url,
'name' : res_post.data.name,
'permalink': 'http://reddit.com' + res_post.data.permalink
};
getOneHtml(post, w, count);
}
last_added = res_post.data.name;
});
scrollLoad = true;
// callback,
// Do something when all dynamically created images have loaded
// see `console`; adjustable
callbacks.fireWith( window, [$(".brick img").size() + " appended to grid, callback at " + $.now()]);
}});
}
// function makeWall() {}
})
jsfiddle http://jsfiddle.net/guest271314/ggsY9/
We've created a jqGrid TreeGrid that represents a filesystem, where branches are folders and leafs are files. We've implemented functionality within the TreeGrid to create new "files" by using addChildNode which works well enough. However, we also want to add functionality to create new folders. Our script works which creates new folders, but they are not immediately displayed on the TreeGrid unless it or the page is reloaded. However, reloading the TreeGrid will collapse all the folders, which is particularly annoying.
Is there a way to selectively refresh the nodes of the TreeGrid, or to add a new branch in that is functional? I've seen some partial documentation on addJSONData, but using this function completely purges the TreeGrid until refresh. I've also attempted to use addChildNode and change certain properties, and I've tried to add in the row manually using DOM manipulation; however, both of these methods break the node that was inserted.
Edit:
var grid = $("#grid");
grid.jqGrid({
treeGrid: true,
treeGridModel: "adjacency",
ExpandColumn: 'name',
ExpandColClick: true,
url:"",
datatype:"json",
colNames:['id','Name','Authorization','Views','Uri'],
colModel:[ {name:'id', index:'id', hidden:true, key:true},
{name:'name', index:'name', sorttype:"text", width:3, sortable:false},
{name:'auth',index:'auth', sorttype:"text", sortable:false, hidden:true},
{name:'views',index:'views', sorttype:"integer", width:1, sortable:false, align:"center"},
{name:'uri',index:'uri',sorttype:'text',sortable:false,hidden:true}],
jsonReader:{ root:"rows"
,page:"page"
,total:"total"
,records:"records"
,repeatitems:false
,cell:""
,id:"0"
,userdata:""
},
multiselect:false,
autowidth:true,
height:"auto",
sortable:false,
toppager:true,
hidegrid: false,
loadui: 'block',
pager:"#grid_pager",
caption: "Files",
});
A returned JSON request for the new folder looks something like this:
ret = {"error":"","total":1,"page":1,"records":1,"rows":[{"id":"1113","name":"test","uri":"accounting\/test\/","parent":1,"isLeaf":false,"expanded":true,"loaded":true}]}
Which I attempt to add in using:
grid[0].addJSONData(ret);
The initial data that is loaded is sent as JSON:
{"rows":[
{"id":"1","uri":"afolder\/","parent_id":"0","name":"afolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"4","uri":"bfolder\/","parent_id":"0","name":"bfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"7","uri":"cfolder\/","parent_id":"0","name":"cfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"20","uri":"dfolder\/","parent_id":"0","name":"dfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"48","uri":"efolder\/","parent_id":"0","name":"efolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"179","uri":"ffolder\/","parent_id":"0","name":"ffolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"182","uri":"gfolder\/","parent_id":"0","name":"gfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"186","uri":"hfolder\/","parent_id":"0","name":"hfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"201","uri":"ifolder\/","parent_id":"0","name":"ifolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"239","uri":"jfolder\/","parent_id":"0","name":"jfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"253","uri":"kfolder\/","parent_id":"0","name":"kfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"262","uri":"lfolder\/","parent_id":"0","name":"lfolder","level":0,"parent":"0","isLeaf":"false"},
{"id":"274","uri":"mfolder\/","parent_id":"0","name":"mfolder","level":0,"parent":"0","isLeaf":"false"}
]}
The demo shows how to use addChildNode method to add tree node. I added in the JSON data which you posted the "loaded":true part because I use in the test no server components and I want to load the treegrid at once.
To show that you should be very careful with ids of the new added row I added two buttons in the demo: "Insert tree node" and "Insert tree node with unique rowid". The first button use the id="1113" from the data which you posted. One click on the button work correct. the second click will insert rows having id duplicates which will be an error. The error you can see different in different web browsers. The second button use $.jgrid.randId() to generate unique rowid. It is probably not an option in your scenario, but it works perfect in case of local tree grid (like in my demo).
Another problem is that you use "parent":"0" in your demo for the root elements. Correct will be "parent":null or "parent":"null" (see the answer). Moreover the property with the name "parent_id" will be ignored. I removed from the demo some settings, so that local sorting can be used in the treegrid.
We solved this problem by extending the functionality of the jqGrid source. First we created a function that could delete all of the child nodes of a particular folder (both folders/branches and files/leaves), so that we could reload them and hence get the latest set of children. This function takes an integer rowid just like delTreeNode().
delChildren : function (rowid) {
return this.each(function () {
var $t = this, rid = $t.p.localReader.id,
left = $t.p.treeReader.left_field,
right = $t.p.treeReader.right_field, myright, width, res, key;
if(!$t.grid || !$t.p.treeGrid) {return;}
var rc = $t.p._index[rowid];
if (rc !== undefined) {
// nested
myright = parseInt($t.p.data[rc][right],10);
width = myright - parseInt($t.p.data[rc][left],10) + 1;
var dr = $($t).jqGrid("getFullTreeNode",$t.p.data[rc]);
if(dr.length>0){
for (var i=0;i<dr.length;i++){
if(dr[i][rid] != rowid)
$($t).jqGrid("delRowData",dr[i][rid]);
}
}
if( $t.p.treeGridModel === "nested") {
// ToDo - update grid data
res = $.jgrid.from($t.p.data)
.greater(left,myright,{stype:'integer'})
.select();
if(res.length) {
for( key in res) {
res[key][left] = parseInt(res[key][left],10) - width ;
}
}
res = $.jgrid.from($t.p.data)
.greater(right,myright,{stype:'integer'})
.select();
if(res.length) {
for( key in res) {
res[key][right] = parseInt(res[key][right],10) - width ;
}
}
}
}
});
},
Then, we created a function to force reloading of a certain node (folder).
reloadNode: function(rc) {
return this.each(function(){
if(!this.grid || !this.p.treeGrid) {return;}
var rid = this.p.localReader.id;
$(this).jqGrid("delChildren", rc[rid]);
var expanded = this.p.treeReader.expanded_field,
parent = this.p.treeReader.parent_id_field,
loaded = this.p.treeReader.loaded,
level = this.p.treeReader.level_field,
lft = this.p.treeReader.left_field,
rgt = this.p.treeReader.right_field;
var id = $.jgrid.getAccessor(rc,this.p.localReader.id);
var rc1 = $("#"+id,this.grid.bDiv)[0];
rc[expanded] = true;
$("div.treeclick",rc1).removeClass(this.p.treeIcons.plus+" tree-plus").addClass(this.p.treeIcons.minus+" tree-minus");
this.p.treeANode = rc1.rowIndex;
this.p.datatype = this.p.treedatatype;
if(this.p.treeGridModel == 'nested') {
$(this).jqGrid("setGridParam",{postData:{nodeid:id,n_left:rc[lft],n_right:rc[rgt],n_level:rc[level]}});
} else {
$(this).jqGrid("setGridParam",{postData:{nodeid:id,parentid:rc[parent],n_level:rc[level]}} );
}
$(this).trigger("reloadGrid");
rc[loaded] = true;
if(this.p.treeGridModel == 'nested') {
$(this).jqGrid("setGridParam",{postData:{nodeid:'',n_left:'',n_right:'',n_level:''}});
} else {
$(this).jqGrid("setGridParam",{postData:{nodeid:'',parentid:'',n_level:''}});
}
});
},
This is the same as expandNode() save for that it does not check if the node was expanded to begin with, and forces it to send an AJAX request for the child elements of that node. This way, we always have the latest children.
Finally, we fixed a small bug in getRowData(), which prevented us from using it to supply the record argument to either expandNode() or our newly created reloadNode(). The issue was that the _id_ field in the JSON return was never created or filled. Adding that in fixed both expandNode() and reloadNode() The following is the changed source. Not ideal, but it works.
getRowData : function( rowid ) {
var res = {}, resall, getall=false, len, j=0;
this.each(function(){
var $t = this,nm,ind;
if(typeof(rowid) == 'undefined') {
getall = true;
resall = [];
len = $t.rows.length;
} else {
ind = $t.rows.namedItem(rowid);
if(!ind) { return res; }
len = 2;
}
while(j<len){
if(getall) { ind = $t.rows[j]; }
if( $(ind).hasClass('jqgrow') ) {
$('td',ind).each( function(i) {
nm = $t.p.colModel[i].name;
if ( nm !== 'cb' && nm !== 'subgrid' && nm !== 'rn') {
if($t.p.treeGrid===true && nm == $t.p.ExpandColumn) {
res[nm] = $.jgrid.htmlDecode($("span:first",this).html());
} else {
if($t.p.colModel[i].key != undefined && $t.p.colModel[i].key == true)
{
try {
res["_" + nm + "_"] = $.unformat(this,{rowId:ind.id, colModel:$t.p.colModel[i]},i);
} catch (e){
res["_" + nm + "_"] = $.jgrid.htmlDecode($(this).html());
}
}
try {
res[nm] = $.unformat(this,{rowId:ind.id, colModel:$t.p.colModel[i]},i);
} catch (e){
res[nm] = $.jgrid.htmlDecode($(this).html());
}
}
}
});
if(getall) { resall.push(res); res={}; }
}
j++;
}
});
return resall ? resall: res;
},
Finally, we pull this all together as follows, with a JSON return object from creating a folder such as
{{"id":"1267", "name":"test15", "uri":"sample1\/test15\/", "parent_id":1, "parent":1, "isLeaf":false}
We call the function like
var parentid = ret.rows[0].parent;
var parent = grid.jqGrid('getRowData', parentid);
grid.jqGrid('reloadNode', parent);
The functions will delete all the child nodes of parent, then send an AJAX request for the new, updated list from the database. I'm going to push this onto the jqGrid Github if possible, since a reload function maybe useful for many people. I've posted it in here in case it isn't approved.