How do I create tiered custom attribute select boxes with SnipCart?

304 Views Asked by At

I am looking to have two custom attributes for products, where the second custom attribute has unique options depending on the first attribute selected.

My particular circumstance is selling art that will have many different formats and sizes, where the price of the sizes will be different depending on which format is selected (IE: Print, Framed, Canvas, etc.).

The obvious alternative is to just have different product listings for each format, and not have the double drop down menu's. However, that is not ideally what I am looking to do.

This is the code provided in the documentation for a single select box and custom attribute:

<label>Frame color</label>
<select id="frame_color">
  <option value="Black">Black</option>
  <option value="Brown">Brown</option>
  <option value="Gold">Gold</option>
</select>
<button
  id="starry-night"
  class="snipcart-add-item"
  data-item-id="starry-night"
  data-item-price="79.99"
  data-item-url="/paintings/starry-night"
  data-item-description="High-quality replica of The Starry Night by the Dutch post-impressionist painter Vincent van Gogh."
  data-item-image="/assets/images/starry-night.jpg"
  data-item-name="The Starry Night"
  data-item-custom1-name="Frame color"
  data-item-custom1-options="Black|Brown|Gold"
  data-item-custom2-name="Gift note">
  Add to cart
</button>
<script>
const button = document.querySelector('#starry-night')
const select = document.querySelector('#frame_color')
select.addEventListener('change', () => {
  // Sets the default frame color when adding the item
  button.setAttribute("data-item-custom1-value", select.value)
})
</script>
4

There are 4 best solutions below

0
On BEST ANSWER

This can be done by having multiple products behind the scenes, but making it appear to the user that there is only one product (per item). Then, changing which products (and custom attribute selection boxes) are visible depending on the first selection box.

I am a novice so there may be a simpler way to accomplish this, but this is what I came up with and it works well. Also, I am using a little jQuery in this code so it will require modifications to work without it.

<style>
.hidden{
  display:none;
}
</style>

<select id="item-1">
    <option value="1">Type 1</option>
    <option value="2">Type 2</option>
</select>

<div id="type-1-1" class="item-content-1">
    <select id="size-1-1">
        <option value="8x8 ($50)">8x8 ($50)</option>
        <option value="12x12 ($100)">12x12 ($100)</option>
        <option value="20x20 ($200)">20x20 ($200)</option>
    </select>
    <button id="add-to-cart-1-1" class="snipcart-add-item"
        data-item-id="product-1-1"
        data-item-price="0.00"
        data-item-url="https://example.com"
        data-item-description="Product 1 type 1 description"
        data-item-image="/img.jpg"
        data-item-name="Product 1 type 1"
        data-item-custom1-name="Size"
        data-item-custom1-options="8x8 ($50)[+50.00]|12x12 ($100)[+100.00]|20x20 ($200)[+200.00]"
        data-item-custom1-value="8x8 ($50)"
        data-item-custom1-required="true">Add To Cart
    </button>
</div>

<div id="type-1-2" class="item-content-1 hidden">
    <select id="size-1-2">
        <option value="8x8 ($90)">8x8 ($90)</option>
        <option value="12x12 ($170)">12x12 ($170)</option>
        <option value="20x20 ($300)">20x20 ($300)</option>
    </select>
    <button id="add-to-cart-1-2" class="snipcart-add-item"
        data-item-id="product-1-2"
        data-item-price="0.00"
        data-item-url="https://example.com"
        data-item-description="Product 1 type 2 description"
        data-item-image="/img.jpg"
        data-item-name="Product 1 type 2"
        data-item-custom1-name="Size"
        data-item-custom1-options="8x8 ($90)[+90.00]|12x12 ($170)[+170.00]|20x20 ($300)[+300.00]"
        data-item-custom1-value="8x8 ($90)"
        data-item-custom1-required="true">Add To Cart
    </button>
</div>

<select id="item-2">
    <option value="1">Type 1</option>
    <option value="2">Type 2</option>
</select>

<div id="type-2-1" class="item-content-2">
    <select id="size-2-1">
        <option value="8x8 ($50)">8x8 ($50)</option>
        <option value="12x12 ($100)">12x12 ($100)</option>
        <option value="20x20 ($200)">20x20 ($200)</option>
    </select>
    <button id="add-to-cart-2-1" class="snipcart-add-item"
        data-item-id="product-2-1"
        data-item-price="0.00"
        data-item-url="https://example.com"
        data-item-description="Product 2 type 1 description"
        data-item-image="/img.jpg"
        data-item-name="Product 2 type 1"
        data-item-custom1-name="Size"
        data-item-custom1-options="8x8 ($50)[+50.00]|12x12 ($100)[+100.00]|20x20 ($200)[+200.00]"
        data-item-custom1-value="8x8 ($50)"
        data-item-custom1-required="true">Add To Cart
    </button>
</div>

<div id="type-2-2" class="item-content-2 hidden">
    <select id="size-2-2">
        <option value="8x8 ($90)">8x8 ($90)</option>
        <option value="12x12 ($170)">12x12 ($170)</option>
        <option value="20x20 ($300)">20x20 ($300)</option>
    </select>
    <button id="add-to-cart-2-2" class="snipcart-add-item"
        data-item-id="product-2-2"
        data-item-price="0.00"
        data-item-url="https://example.com"
        data-item-description="Product 2 type 2 description"
        data-item-image="/img.jpg"
        data-item-name="Product 2 type 2"
        data-item-custom1-name="Size"
        data-item-custom1-options="8x8 ($90)[+90.00]|12x12 ($170)[+170.00]|20x20 ($300)[+300.00]"
        data-item-custom1-value="8x8 ($90)"
        data-item-custom1-required="true">Add To Cart
    </button>
</div>

<script>
    var item_count = 2; //number of items in shop
    var type_count = 2; //number of type subdivisions
    var i = 1;
    for (i = 1; i <= item_count; i++) {
        const type_select = document.querySelector('#item-' + i);
        const count = i
        type_select.addEventListener('change', () => {
            $('.item-content-' + count).addClass("hidden");
            $('#type-' + count + "-" + type_select.value).removeClass("hidden");
        });
        var ii = 1;
        for (ii = 1; ii <= type_count; ii++) {
            const addToCart = document.querySelector('#add-to-cart-' + i + "-" + ii);
            const size = document.querySelector('#size-' + i + "-" + ii);
            size.addEventListener('change', () => {
                addToCart.setAttribute("data-item-custom1-value", size.value);
            });
        };
    };
</script>
0
On

I believe you can do this with data-item-custom1-value:

data-item-custom1-name="Color"
data-item-custom1-options="Blue|Dark Gray|Light Gray"
data-item-custom1-value="Dark Gray"

Then use Javascript to change the data-item-custom1-value when a select is changed to the value of the select.

Complete Example

<div>
    <label for="color">Color</label>
    <select name="color" id="color">
        <option value="Blue">Blue</option>
        <option value="Dark Gray">Dark Gray</option>
        <option value="Light Gray">Light Gray</option>
    </select>
</div>

<button id="poster-cart-btn" class="snipcart-add-item btn btn-dark"
data-item-id="1"
data-item-price="9.95"
data-item-url="/"
data-item-name="Poster"
data-item-image="img/poster.png"
data-item-description="A pretty poster"
data-item-custom1-name="Color"
data-item-custom1-options="Blue|Dark Gray|Light Gray"
data-item-custom1-value="Blue">
    Add to cart
</button>
    
<script type="text/javascript">
    window.addEventListener('load', function() {
        document.getElementById('color').onchange = function() {
            const val = document.getElementById('color').value;
            document.getElementById('poster-cart-btn').setAttribute('data-item-custom1-value', val);
        }
    });
</script>
1
On

iam not realy good in js, is there anyway to give .setAttribute more then 1 value to altering the price? Thanks alot

 <select id="size-2-2">
    <option value="8x8 ($90)">8x8 ($90)</option>
    <option value="12x12 ($170)">12x12 ($170)</option>
    <option value="20x20 ($300)">20x20 ($300)</option>
</select>
<select id="color-2-3">
    <option value="color_1 ($90)">8x8 ($90)</option>
    <option value="color_2 ($170)">12x12 ($170)</option>
    <option value="color_3 ($300)">20x20 ($300)</option>
</select>

<button id="add-to-cart-2-2" class="snipcart-add-item"
    data-item-id="product-2-2"
    data-item-price="0.00"
    data-item-url="https://example.com"
    data-item-description="Product 2 type 2 description"
    data-item-image="/img.jpg"
    data-item-name="Product 2 type 2"
    data-item-custom1-name="Size"
    data-item-custom1-options="8x8 ($90)[+90.00]|12x12 ($170)[+170.00]|20x20 ($300)[+300.00]"
    data-item-custom1-value="8x8 ($90)"
    data-item-custom1-required="true" 
    data-item-custom2-name="Color"
    data-item-custom2-options="color_1 ($90)[+90.00]|color_2 ($170)[+170.00]|color_3($300)[+300.00]"
    data-item-custom2-value="color_1 ($90)"
    data-item-custom2-required="true">Add To Cart
</button>

...
addToCart.setAttribute("data-item-custom1-value", size.value);
addToCart.setAttribute("data-item-custom2-value", size.value);
...

0
On

Thanks a lot @justin I modify your way dirty and it works fine for me! Now snipcart snacks all the values from the select fields with one button. I know there is a better way to load "window.addEventListener" x times. Feel free to post a better and simpler solution.

<div>
    <label for="color-1">Color 1</label>
    <select name="Color select 1" id="color_1">
        <option value="color_1_1">0</option>
        <option value="color_1_2">+11</option>
        <option value="color_1_3">+111</option>
    </select>
    
    <label for="color-2">Color 2</label>
    <select name="Color select 2" id="color_2">
        <option value="color_2_1">0</option>
        <option value="color_2_2">+12</option>
        <option value="color_2_3">+112</option>
    </select>
    
    <label for="color-3">Color 3</label>
    <select name="Color select 3" id="color_3">
        <option value="color_3_1">0</option>
        <option value="color_3_2">+13</option>
        <option value="color_3_3">-113</option>
    </select>
    
</div>

<button id="poster-cart-btn" class="snipcart-add-item"
      data-item-id="1"
      data-item-price="0.00"
      data-item-url="/"
      data-item-name="Poster"
      data-item-image="img/poster.png"
      data-item-description="A pretty poster"
      data-item-custom1-name="Color 1"
      data-item-custom1-options="color_1_1|color_1_2[+11.00]|color_1_3[+111.00]"
      data-item-custom1-value="color_1_1"
      data-item-custom2-name="Color 2"
      data-item-custom2-options="color_2_1|color_2_2[+12.00]|color_2_3[+112.00]"
      data-item-custom2-value="color_2_1"
      data-item-custom3-name="Color 3"
      data-item-custom3-options="color_3_1|color_3_2[+13.00]|color_3_3[+113.00]"
      data-item-custom3-value="color_3_1">
      Add to cart
</button>
    
<script type="text/javascript">
    window.addEventListener('load', function() {
    
        document.getElementById('color_1').onchange = function() {
            const val = document.getElementById('color_1').value;
            document.getElementById('poster-cart-btn').setAttribute('data-item-custom1-value', val);
        }
         document.getElementById('color_2').onchange = function() {
            const val = document.getElementById('color_2').value;
            document.getElementById('poster-cart-btn').setAttribute('data-item-custom2-value', val);
        }
        
         document.getElementById('color_3').onchange = function() {
            const val = document.getElementById('color_3').value;
            document.getElementById('poster-cart-btn').setAttribute('data-item-custom3-value', val);
        }
    });
</script>