How to insert before previous element using jquery?

I have select with some hidden options and two buttons: up and down. To hide options I have to wrap them into invisible span, because in some browsers <option style="display: none;"> just doesn’t work. When I press up or down button I have to move selected option in select list up or down. Recently I’ve done it like this:

function moveOption(option, up) {
  if (up) {
    option.insertBefore(option.prev());
  } else {
    option.insertAfter(option.next());
  }
}

But the problem is - If I have hidden options I have to press button several times to switch it with visible option, because each time button is pressed I’ve got option switched with invisible one. To fix this I’ve already tried:

option.insertBefore(option.prev(':not(.hidden)'));
option.insertBefore(option.prev('option'));
option.insertBefore(option.prev(':is(visible)'));

But none of it seams to work. Here’s the js fiddle.

$('#up').click(function() {
  var option = $('select').children('option:selected');
  moveOption(option, true);
});

$('#down').click(function() {
  var option = $('select').children('option:selected');
  moveOption(option, false);
});

function moveOption(option, up) {
  if (up) {
    option.insertBefore(option.prev());
  } else {
    option.insertAfter(option.next());
  }
}
.hidden {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select size="10">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<span class="hidden"><option class="hidden" value="4">Four</option></span>
<span class="hidden"><option class="hidden" value="5">Five</option></span>
<span class="hidden"><option class="hidden" value="6">Six</option></span>
<span class="hidden"><option class="hidden" value="7">Seven</option></span>
<span class="hidden"><option class="hidden" value="8">Eight</option></span>
<option value="9">Nine</option>
<option value="10">Ten</option>
</select>
<input type="button" value="Up" id="up" />
<input type="button" value="Down" id="down" />

solution

The reason your example isn’t working is because the .next()/.prev() methods only look at the immediately following and preceding elements respectively.

If one of the immediately following/preceding elements are hidden, nothing is selected.

You need to use the methods .prevAll(':visible')/ .nextAll(':visible') in order to select the previous/next visible sibling elements.

Then chain the .first() method in order to select the first match.

Updated Example

function moveOption(option, up) {
  if (up) {
    option.insertBefore(option.prevAll(':visible').first());
  } else {
    option.insertAfter(option.nextAll(':visible').first());
  }
}

Rather than using the .first() method, you could also just use the :first jQuery selector:

function moveOption(option, up) {
  if (up) {
    option.insertBefore(option.prevAll(':visible:first'));
  } else {
    option.insertAfter(option.nextAll(':visible:first'));
  }
}