Fount it.
So my solution is each time the option is clicked, we add the option value to a html tag called data-new, then we catch that data-new in multiselect.js, and replace it with data, which will be saved to the database some how.
To do this, you will need 2 js files.
Note: shipping_methods_no_free is attribute code
web/js/custom.js
require([
'jquery'
], function ($) {
'use strict';
$( document ).ready(function() {
$(document).on('mousedown', 'div[data-index="shipping_methods_no_free"] select', function(e){
// use mousedown instead of change, don't know why
var currentData = [];
if ($(this).attr('data-new') === undefined) {
// first load
currentData = $(this).val();
} else if ($(this).attr('data-new') === "") {
// if data-new is null
currentData = [];
} else if ($(this).attr('data-new') !== "") {
// if data-new is not null
currentData = $(this).attr('data-new').split(',');
}
var selected = e.target.value;
if ($.inArray(selected, currentData) !== -1) {
// remove selected from array
currentData = jQuery.grep(currentData, function(value) {
return value != selected;
});
// style the option again, use linear-gradient and -webkit-text-fill-color to pass "user agent stylesheet" problem
$('div[data-index="shipping_methods_no_free"] select option[value="' + selected + '"]')
.css('background', 'linear-gradient(#FFF, #FFF)');
$('div[data-index="shipping_methods_no_free"] select option')
.css('-webkit-text-fill-color', 'rgb(16, 16, 16)');
} else {
// add selected to array
currentData.push(selected);
// style the option again, use linear-gradient and -webkit-text-fill-color to pass "user agent stylesheet" problem
var i;
for (i = 0; i < currentData.length; ++i) {
$('div[data-index="shipping_methods_no_free"] select option[value="' + currentData[i] + '"]')
.css('background', 'linear-gradient(rgb(206, 206, 206), rgb(206, 206, 206))')
.css('-webkit-text-fill-color', 'rgb(16, 16, 16)');
}
$('div[data-index="shipping_methods_no_free"] select option[value="' + selected + '"]')
.css('-webkit-text-fill-color', '#fff');
}
$(this).attr('data-new', currentData.toString());
}).on('mousemove', 'div[data-index="shipping_methods_no_free"] select', function(e){e.preventDefault()});
});
});
Magento_Ui/web/js/form/element/multiselect.js
other contents of this file are the same, only need to override setPrepareToSendData()
setPrepareToSendData: function (data) {
if (_.isUndefined(data) || !data.length) {
data = '';
}
// START custom
var element = document.getElementsByName('vendor[shipping_methods_no_free]');
if (element.length) {
// custom add new data so it can post the correct data.
var newData = element[0].getAttribute('data-new');
var newData = newData.split(",");
data = newData;
}
// END custom
this.source.set(this.dataScope + '-prepared-for-send', newData);
},