The first time you load a page with configurable products, no attributes are selected. But what if you want the options to be selected by default to promote this product configuration or for any other reasons? This is an issue with a solution.
A product attributes data is passed into spConfig object. The object is initialized in the template
template/catalog/product/view/type/options/configurable.phtml
1 2 3 |
<script type="text/javascript"> var spConfig = new Product.Config(<?php echo $this->getJsonConfig() ?>); </script> |
Attributes configuration object has the following structure:
Magento doesn’t build all the <select> elements with PHP. It rebuilds <select> with the help of JS as soon as any attribute changes. In .phtml template mentioned above we can see that <select> tags and one option only are formed with PHP.
1 2 3 |
<select name="super_attribute[<?php echo $_attribute->getAttributeId() ?>]" id="attribute<?php echo $_attribute->getAttributeId() ?>" class="required-entry super-attribute-select"> <option><?php echo $this->__('Choose an Option...') ?></option> </select> |
Preselection therefore should be realized by using JS. Let’s open the following file:
js/varien/configurable.js
In object initialization method we can see observer tagging the “change” event of each of the attributes selects.
1 2 3 |
this.settings.each(function(element){ Event.observe(element, 'change', this.configure.bind(this)) }.bind(this)); |
Event response triggers selects rebuilding by the script. We can see it in the method below:
1 2 3 4 |
configure: function(event){ var element = Event.element(event); this.configureElement(element); }, |
As well as select configuring and rebuilding method itself
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
configureElement : function(element) { this.reloadOptionLabels(element); if(element.value){ this.state[element.config.id] = element.value; if(element.nextSetting){ element.nextSetting.disabled = false; this.fillSelect(element.nextSetting); this.resetChildren(element.nextSetting); } } else { this.resetChildren(element); } this.reloadPrice(); }, |
It makes no sense to follow all the called methods. We have the required data.
To preselect attributes we need the following source data:
1. Attributes in the priority queue (the choice of the next attribute depends on the choice of the previous one)
Here is the example of the simple array with attributes in the priority queue:
Array was sorted with PHP; we are not going into all of the uses of the script.
2. Attributes ID and their options values
In the current example, it is an object with the following structure
We’re interested in Conditions field only. As we see, “conditions” is an object with data
Attribute Id => Option Value
Let’s check out the select element to ensure that existing data is enough.
There are two ways to preselect attributes:
1. Select the option in attribute select alternately and call the select rebuild method.
2. Emulate the alternate option selection in the attributes.
Variant 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//combinationId – The required combination number; in the current example = 1 function setDefaultConfigCheapestOptions(combinationId) { rulesKeys = Object.keys(rulesConfig); correlation = rulesConfig[combinationId]['conditions']; // Looping through all the attributes for (i=0; i<attrOrder.length;i++) { optionId = correlation[attrOrder[i]]; atrStr = 'attribute'+attrOrder[i]; select = $(atrStr); for(var k=0;k<select.length;k++){ // Looking for the option with the required value if (select[k].readAttribute('value') == optionId) { //Selecting the option select[k].selected = true; //Calling selects attributes rebuild methods spConfig.configureElement(select); break; } } } } |
Variant 2 :
As a matter of fact, the second variant is same as the first one, except in the first example we implicitly call the spConfig.configureElement(select) method; but we might as well use the selects choosing emulator by making beforehand the option selected.
Event.simulate(select, ‘change’);
This way is better as Magento methods are not used; file root/js/varien/configurable.js source code replacement will therefore be negligible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
(function(){ var eventMatchers = { 'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, 'MouseEvents': /^(?:click|mouse(?:down|up|over|move|out))$/ } var defaultOptions = { pointerX: 0, pointerY: 0, button: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, bubbles: true, cancelable: true } Event.simulate = function(element, eventName) { var options = Object.extend(defaultOptions, arguments[2] || { }); var oEvent, eventType = null; element = $(element); for (var name in eventMatchers) { if (eventMatchers[name].test(eventName)) { eventType = name; break; } } if (!eventType) throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported'); if (document.createEvent) { oEvent = document.createEvent(eventType); if (eventType == 'HTMLEvents') { oEvent.initEvent(eventName, options.bubbles, options.cancelable); } else { oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element); } element.dispatchEvent(oEvent); } else { options.clientX = options.pointerX; options.clientY = options.pointerY; oEvent = Object.extend(document.createEventObject(), options); element.fireEvent('on' + eventName, oEvent); } return element; } Element.addMethods({ simulate: Event.simulate }); })() |
The second variant works properly in Chrome, IE and Opera browsers. There are issues in Firefox though: not all the attributes are preselected (as a rule, 2-3 ones only). This is due to the fact that looping through the attributes takes more time than selects rebuild process.
According to js/varien/configurable.js (Variant 2):
Where
(function(){ …
should be placed (replaced)?