Reputation: 5914
I have a user interface where a user can move multiple options between two select lists. It looks just like this mock up here:
You select one or multiple items then press the arrow button and it moves them into the other list.
The problem is I have a sort function to resort the options after they've been moved and really weird stuff is happening. When you first move options from the left to the right it works fine. If you move another item from the left to right it overwrites what is already there. BUT if you click on the left list again the items appear like normal! If you inspect the dom you can see the items are there, there is no css rules that would make them not show up, and it doesn't look like any extra css is applied when they show up again. The code to move the items over looks like this:
$('#moveRight').click(function () {
// Get all selection options
var selectedOptions = $('#from option:selected');
// Let's remove our items from the from list...
$('#from option:selected').remove();
// Add them to the to list...
$('#to').append(selectedOptions);
// Sort alphabetically
$('#to option').sort(sortAlpha).appendTo('#to');
});
I created this codepen with a working example of the interface:
https://codepen.io/onekidney_/pen/YzyrYKX
Steps to reproduce:
Notice that the new items are the only one that show up in the list BUT if you click the left list again they all appear! The error appears to be in the sorting because removing that fixes this issue.
This question had the same issue but it appears that they fixed it by removing the sort. This is new behavior - it didn't do this years ago when this code was deployed (which is why jquery is used btw.) I still would like the feature of having the options sorted after they've been moved. Does anyone have an idea of what's going on here?
Upvotes: 1
Views: 425
Reputation: 337580
This appears to be a browser rendering bug. I'm unable to figure out exactly why it happens, but it appears that the use of sort logic after the quick detach/remove/append operations causes the browser renderer to get confused. If you look at the DOM inspector you can see the option
element are in the correct place, however the renderer does not display them.
I'd suggest raising this as a bug with the browser vendors.
The only workaround I've found for this is to trigger a focus
event on the target select
to force the element to be re-drawn in the DOM. The blur
is not necessary for this fix, it's only to avoid the CSS outline appearing around the selected element which is a little jarring.
Also note that I genericised the logic using data
attributes on both select
elements and buttons.
let els = {
from: $('#from'),
to: $('#to')
};
$('.move').click(function() {
let $target = els[this.dataset.target];
let $source = els[this.dataset.source];
$source.children('option:selected').appendTo($target);
$target.children('option').sort(sortAlpha).appendTo($target);
$target.focus().blur();
});
let sortAlpha = (a, b) => parseInt(a.textContent, 10) > parseInt(b.textContent, 10) ? 1 : -1;
body {
background-color: #a3d5d3;
}
select {
width: 200px;
height: 200px;
}
button {
width: 100px;
padding: 5px;
margin: 0 5px 5px 5px;
}
#button-container {
display: inline-block;
vertical-align: top;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select id="from" multiple>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
<div id="button-container">
<button class="move" id="moveRight" data-target="to" data-source="from">-></button><br />
<button class="move" id="moveLeft" data-target="from" data-source="to"><-</button>
</div>
<select id="to" multiple>
</select>
Upvotes: 1