4

Please try the jsFiddle with jQuery UI selectmenu and 2 buttons.

With the help of 2 buttons prevGame and nextGame I am able to change the selectedIndex variable tracking the currently selected game number.

The jQuery UI selectmenu doc unfortunately does not explain how to set and get (so that I can update the span currGame) the currently selected item:

screenshot

Please explain: how to set and get the selected item in jQuery UI selectmenu?

HTML-code:

<form>
  <select name="games" id="games"></select>
  <button id="prevGame">&lt;</button>
  <span id="currGame">Loading...</span>
  <button id="nextGame">&gt;</button>
</form>

JavaScript-code:

var yourGames = [1, 3, 5];
var hisGames = [8, 10, 12, 14];
var selectedIndex = 0;

$("#games").selectmenu();

// emulate repeating server responses
setInterval(function() {
  updateMenu();
}, 5000);

$('#prevGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.max(selectedIndex - 1, 0);
  updateButtons();
});

$('#nextGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.min(selectedIndex + 1, lastIndex());
  updateButtons();
});

function lastIndex() {
  return yourGames.length + hisGames.length - 1;
}

function updateButtons() {
  $('#currGame').html('selectedIndex=' + selectedIndex); // TODO: change to "Game #"
  $('#prevGame').button(selectedIndex == 0 ? "disable" : "enable");
  $('#nextGame').button(selectedIndex == lastIndex() ? "disable" : "enable");
}

function updateMenu() {
  var yourGroup = ['<optgroup label="YOUR TURN">'];
  for (var i = 0; i < yourGames.length; i++) {
    var gameNumber = yourGames[i];
    var selectedTag = (i == selectedIndex ? 'selected="selected"' : '');
    yourGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  yourGroup.push('</optgroup>');

  var hisGroup = ['<optgroup label="HIS TURN">'];
  for (var i = 0; i < hisGames.length; i++) {
    var gameNumber = hisGames[i];
    var selectedTag = (i - yourGames.length == selectedIndex ? 'selected="selected"' : '');
    hisGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  hisGroup.push('</optgroup>');

  $("#games").selectmenu('destroy')
    .empty()
    .append(yourGroup.length > 2 ? yourGroup.join('') : '')
    .append(hisGroup.length > 2 ? hisGroup.join('') : '')
    .selectmenu(); // TODO: select the game at selectIndex
}

UPDATE:

I have prepared a newer jsFiddle using selectmenu("refresh") instead of selectmenu("destroy"), but it still has some issues.

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
  • 1
    >>Did you want master/slave? ex. `#currGame` is clicked and changes value, then `#games` changes to match `#currGame`. But not the other way around. >>Or synchronized peers? If one changes value then the other one does as well. The changes are effective vice versa as well. – zer00ne May 01 '16 at 14:30
  • I am trying to get it working both ways: **(selectmenu -> span + buttons)** if an item is selected in the selectmenu - then it is shown in `span#currGame` and the 2 buttons are enabled or disabled; **(buttons -> span + selectmenu)** if the buttons are clicked, then the `span#currGame` is updated and the item is selected in the selectmenu too. Also, there is a method called by `setInterval`, emulating server callbacks. – Alexander Farber May 01 '16 at 14:40
  • 1
    I got the older version working...but in slave/master. I will switch over to your newer version and make them peers. I might have to change your server simulation to debounce because timeIntervaled updates can degrade overall performance and/or function. – zer00ne May 01 '16 at 14:46

3 Answers3

1

This can be done in much better way but the following code provides what you asked for:

var yourGames = [1, 3, 5];
var hisGames = [8, 10, 12, 14];
var selectedIndex = 0;

$("#games").selectmenu();

$('#prevGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.max(selectedIndex - 1, 0);
  updateMenu();
  updateButtons();
});

$('#nextGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.min(selectedIndex + 1, lastIndex());
  updateMenu();
  updateButtons();
});

function lastIndex() {
  return yourGames.length + hisGames.length - 1;
}

function updateButtons() {
    var selectedText = $("#games option:selected").text();
  $('#currGame').html(selectedText);
  $('#prevGame').button(selectedIndex == 0 ? "disable" : "enable");
  $('#nextGame').button(selectedIndex == lastIndex() ? "disable" : "enable");
}

function updateMenu() {
  var yourGroup = ['<optgroup label="YOUR TURN">'];
  for (var i = 0; i < yourGames.length; i++) {
    var gameNumber = yourGames[i];
    var selectedTag = (i == selectedIndex ? 'selected="selected"' : '');
    yourGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  yourGroup.push('</optgroup>');

  var hisGroup = ['<optgroup label="HIS TURN">'];
  for (var i = 0; i < hisGames.length; i++) {
    var gameNumber = hisGames[i];
    var selectedTag = (yourGames.length + i == selectedIndex ? 'selected="selected"' : '');
    hisGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  hisGroup.push('</optgroup>');

  console.log(yourGroup);
  console.log(hisGroup);

  $("#games").selectmenu('destroy')
    .empty()
    .append(yourGroup.length > 2 ? yourGroup.join('') : '')
    .append(hisGroup.length > 2 ? hisGroup.join('') : '')
    .selectmenu();
}

I also updated your Fiddle so you can play with it. https://jsfiddle.net/q07uarwr/35/

Tarek N. Elsamni
  • 1,718
  • 1
  • 12
  • 15
  • Thank you, Tarek! What is the URL of your jsFiddle please? Also I would prefer not to call `updateMenu()` on button clicks - because it destroys the jQuery UI selectmenu **and thus closes it**... The `updateMenu()` should only be called by periodical server responses - when the list of games really changes. – Alexander Farber Apr 29 '16 at 13:41
  • 1
    So That's an updated version: https://jsfiddle.net/q07uarwr/36/ It only call updateMenu() as before. – Tarek N. Elsamni Apr 29 '16 at 13:51
  • Unfortunately your solution does not look good: it still requires destroying and then rebuilding the selectmenu in order to change the selected item. This only happens every 5 seconds in [your jsFiddle](https://jsfiddle.net/q07uarwr/36/) and feels awkward (there is lag between clicking a button and changing the selected item in menu)... There must be a better way. – Alexander Farber Apr 29 '16 at 13:57
  • My solution is like this because you coded it like this and the final required result is not clear for me :) But I got what you are trying to do and will update it. – Tarek N. Elsamni Apr 29 '16 at 13:59
1

I gave it a try and got both updating and displaying text. But only thing you need to find next is how to set value once selected index changes to the next <option> group in the drop down.

This is the main change:

function updateButtons() {
  var gamesOptions = $('#games option');
  $('#currGame').html("<span>" + $(gamesOptions[selectedIndex]).text() + "</span>");
  $("#games").val(selectedIndex).change();
  $('#prevGame').button(selectedIndex == 0 ? "disable" : "enable");
  $('#nextGame').button(selectedIndex == lastIndex() ? "disable" : "enable");
  updateMenu();
}

https://jsfiddle.net/q07uarwr/34/

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Riddell
  • 1,429
  • 11
  • 22
1

jQuery and jQuery UI provides no way to directly set selected index of a select menu. You can use pure javascript way to set the selected index. Also I assume you want to change the text between buttons every time select menu changes. You can do it like so:

var yourGames = [1, 3, 5];
var hisGames = [8, 10, 12, 14];
var selectedIndex = 0;

setInterval(function() {
  updateMenu();
  updateCurrentGame();
  updateButtons();
}, 5000);

$("#games").selectmenu();

$('#prevGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.max(selectedIndex - 1, 0);
  updateButtons();
  updateCurrentGame();
});

$('#nextGame').button().click(function(e) {
  e.preventDefault();
  selectedIndex = Math.min(selectedIndex + 1, lastIndex());
  updateButtons();
  updateCurrentGame();
});

function lastIndex() {
  return yourGames.length + hisGames.length - 1;
}

function updateButtons() {
  $('#prevGame').button(selectedIndex == 0 ? "disable" : "enable");
  $('#nextGame').button(selectedIndex == lastIndex() ? "disable" : "enable");
}

// Update the select menu when prev & next buttons are pressed
function updateCurrentGame() {
  var selectedText = $($("select#games option")[selectedIndex]).text();
  $('#currGame').html(selectedText);
  // pure js vay to set selected index
  $("#games")[0].selectedIndex = selectedIndex;
  $("#games").selectmenu("refresh");
}

// Update the selected index every time the select menu is changed manually
$("#games").on("selectmenuchange", function(e, ui) {
  console.log(ui);
  selectedIndex = ui.item.index;
  var selectedText = ui.item.element.text();
  $('#currGame').html(selectedText);
  updateButtons();
})

function updateMenu() {
  var yourGroup = ['<optgroup label="YOUR TURN">'];
  for (var i = 0; i < yourGames.length; i++) {
    var gameNumber = yourGames[i];
    var selectedTag = (i == selectedIndex ? 'selected="selected"' : '');
    yourGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  yourGroup.push('</optgroup>');

  var hisGroup = ['<optgroup label="HIS TURN">'];
  for (var i = 0; i < hisGames.length; i++) {
    var gameNumber = hisGames[i];
    var selectedTag = (yourGames.length + i == selectedIndex ? 'selected="selected"' : '');
    hisGroup.push(
      '<option ' +
      selectedTag +
      ' value="' +
      gameNumber +
      '">Game #' +
      gameNumber +
      '</option>');
  }
  hisGroup.push('</optgroup>');

  $("#games").selectmenu('destroy')
    .empty()
    .append(yourGroup.length > 2 ? yourGroup.join('') : '')
    .append(hisGroup.length > 2 ? hisGroup.join('') : '')
    .selectmenu();
}
button#prevGame,
span#currGame,
button#nextGame,
button#newGame {
  vertical-align: top;
}
select#games {
  width: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.min.css" rel="stylesheet" />

<form>
  <select name="games" id="games"></select>
  <button id="prevGame">&lt;</button>
  <span id="currGame">Loading...</span>
  <button id="nextGame">&gt;</button>
</form>
Gokhan Kurt
  • 8,239
  • 1
  • 27
  • 51
  • 1
    Thanks! IMHO in the `selectmenuchange` callback you could better use `ui.item.index` and `ui.item.value` and also enabling/disabling the buttons seems to be missing there... – Alexander Farber May 01 '16 at 15:40
  • 1
    @AlexanderFarber True. Made the necessary edits. I didn't know you could use `ui.item.index` like that. I thought it would also include index of `optgroup`. – Gokhan Kurt May 01 '16 at 17:44
  • Your idea to use `$("#games")[0].selectedIndex` is good, thanks! Also I think it is better to use `selectmenu("refresh")` instead of `selectmenu("destroy")` - here is [my current jsFiddle](https://jsfiddle.net/afarber/n42stqsv/). – Alexander Farber May 01 '16 at 17:47
  • I didn't look into `updateMenu`. It is up to you how you update it and it is not asked in the question. However, I wouldn't update my menu like that, that's given. – Gokhan Kurt May 01 '16 at 17:52