serioja
serioja

Reputation: 97

Selectively adding and removing values of same CSS property

I’m creating an Open Type features tester using jquery and want to give the user the possibility of seeing several open type features applied at the same time.

The first dropdown sets the property to font-feature-settings: ‘frac’ 1;, he second dropdown sets the property to font-feature-settings: ‘smcp’ 1;

Currently if both of the dropdowns are active one overrides the instructions of the other one. What I need is that if both of them are active at the same time the property is set to font-feature-settings: ‘frac’ 1, ‘smcp’ 1; and consequently if only one of them is deactivated only the corresponding value is removed from the property.

Note: I'm aware that the Open Type features don't work when I link the font from the Google fonts site, so I've been testing it with it installed.

Thanks in advance.

.font-tester{
      color: #000;
      line-height: normal;
      width: 100%;
      word-wrap: break-word;
      padding-top: 30px;
  }
<style> @import url('https://fonts.googleapis.com/css2?family=EB+Garamond&display=swap'); </style>

<div class="tester-container">
    
  <select id="tester-figures">
          <option value="none" selected>Default</option>
          <option value="'frac' 1">Fractions</option>
  </select>

  <select id="tester-smcp">
    <option value="none" selected>Default</option>
    <option value="'smcp' 1">Small caps</option>
  </select>
    
    <div class="font-tester" contenteditable="true" spellcheck="false" style="font-family: 'EB Garamond', serif; font-size:65pt;">
abc 123
    </div>
  
</div>
  
  <script>      
    $("#tester-figures").change(function() {
        $('.font-tester').css("font-feature-settings", $(this).val());
    });
    
    $("#tester-smcp").change(function() {
        $('.font-tester').css("font-feature-settings", $(this).val());
    });
    
  </script>

Upvotes: 2

Views: 64

Answers (2)

Brebber
Brebber

Reputation: 3084

There are many ways to solve a task. Additional here is an alternative solution which has less and more simplyfied and code.

It works with only one function which add the change event to both selects. On change it reads out the actual values from both selects and builds a simple css string based on the actual values of both selects.

$('#tester-figures').add('#tester-smcp').each( function(){
    const $this = $(this);
    $this.change(function(){

        let actualVals = {};
        actualVals.TesterFigures = $('#tester-figures').val();
        actualVals.TesterSmcp = $('#tester-smcp').val();                    
        
        let myVal = '';

        if('none' == actualVals.TesterFigures){
            myVal = 'none' == actualVals.TesterSmcp  ?  '' : actualVals.TesterSmcp;
        }else {
            myVal = 'none' == actualVals.TesterSmcp  ?  actualVals.TesterFigures : actualVals.TesterFigures + ', ' + actualVals.TesterSmcp;
        }

        $('.font-tester').css("font-feature-settings", myVal);

    });

});

NOTE: I don't have worked with css attribute font-feature-settings yet. So I don't have any experience with this special item. What I notice is, that the strings in the selects (i.e. 'frac' 1) changes. The inner quotes and the number get lost when the css is added to the html element. But the style still works without them. Maybe you like to check if it works if you only use frac and smcp in your selects. In actual FF it works ;-)

Upvotes: 1

cjl750
cjl750

Reputation: 4629

What you need to do is basically concatenate the values into a comma-separated string as you update them, instead of just whatever the input value is. You could write your own function to do this if you wanted to. It would have to include logic for whether there is only one value or multiple values in order to make sure you don't end up with a trailing comma for no reason. However, there's an easier way: if we store the values in an array, we can use Array.join(), which will automagically handle that logic for us.

Array.join() joins all the elements in an array into one big string, and it takes an optional parameter that specifies how to join them. So we can do like so:

['one', 'two', 'three'].join(', '); // => 'one, two, three'

Note how there's not an extra comma and space at the end.

So now that we know how to get the end result, we just need to work on getting the values. You will need to either add elements to the array or remove them based on how you interact with the dropdowns. Since you only have two items in each dropdown right now, that logic can be pretty simple. If you are going to have multiple items in any dropdowns, the overall approach is the same, but you'll have to tweak the if…else part in the snippet below.

Keep in mind that, if you want to remove an item from the array, you need to know what to remove. But that's the old value. If you run $(myInput).val() inside your .change() function, you're going to get the current value. So you need to keep track of the old values separately.

Once you have the new array set up, you just have to apply the Array.join() magic and then you have your string that you can use in the jQuery .css() function.

It will be much easier to maintain this if you cut out as much repeated logic as possible and just have one single function that the inputs can each call. So I've done that for you below. You can add more inputs to the .each() function by chaining on more .add()s before it, if you need to.

let oldValues = ['none', 'none']; // whatever the default values are for each select
let currentValue = [];

const getNewValue = input => {
  const val = $(input).val();
  const index = input.index();
  const oldValue = oldValues[index];
  if (val !== 'none') {
    currentValue.push(val);
  } else {
    const indexOfValue = currentValue.indexOf(oldValue);
    currentValue.splice(indexOfValue, 1);
  }
  oldValues[index] = val;
  const newCssValue = currentValue.join(', ');
  console.clear();
  console.log(`new value is "${newCssValue}"`);
  return newCssValue;
};

$(function() {
  const $fontTester = $('.font-tester');
  $('#tester-figures').add('#tester-smcp').each(function() {
    const $this = $(this);
    $this.change(function() {
      const newCssValue = getNewValue($this);
      $fontTester.css('font-feature-settings', newCssValue);
    });
  });
});
.font-tester {
  color: #000;
  line-height: normal;
  width: 100%;
  word-wrap: break-word;
  padding-top: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="tester-container">
  <select id="tester-figures">
    <option value="none" selected>Default</option>
    <option value="'frac' 1">Fractions</option>
  </select>
  <select id="tester-smcp">
    <option value="none" selected>Default</option>
    <option value="'smcp' 1">Small caps</option>
  </select>
  <div class="font-tester" contenteditable="true" spellcheck="false" style="font-family: 'EB Garamond', serif; font-size:65pt;">
    abc 123
  </div>
</div>

Upvotes: 1

Related Questions