0

I want to add some clickable options to a select2 list, but it seems like it removes the mousedown event. Try the snippet below and click on one of the 1, 2, 3. Why doesn't the alert open?

$(".form-control").select2({
  templateResult: formatSelect2,
});

function formatSelect2 (data) {
  if (!data.id) { return data.text; }
  var $data = $(
    '<span data-status="' + data.element.getAttribute("data-status") + '">' + data.text + '<span class="statuses"><span data-status="1">1</span><span data-status="2">2</span><span data-status="3">3</span></span></span>'
  );
  return $data;
};

$(document).on('mousedown', '.statuses span', function(e) {
  alert('Why isn\'t this opening?');
});
.statuses {
  margin-left: 8px;
}
.statuses span {
  margin-left: 8px;
  cursor: pointer;
}
.statuses span:hover {
  margin-left: 8px;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<select class="form-control" style="width: 300px">
  <option selected="selected" data-status="1">orange</option>
  <option>white</option>
  <option>purple</option>
</select>
SeaBass
  • 1,584
  • 4
  • 20
  • 46

1 Answers1

1

Yes, you're right. Select2 does remove the events on the dropdown container. Here's the line in the library that does that:

this.$dropdownContainer.on('mousedown', function (evt) {
  evt.stopPropagation();
});

Solution:

  1. Get the instance of select2
  2. Bind events and check if the mousedown is on the status's span using jQuery closest.

Here's how:

var selectInst = $(".form-control").data('select2');

selectInst.$results.on('mousedown', function (e) {
   if($(e.target).closest('.statuses span').length) {
     var data = $(e.target).parents('li[aria-selected]').data('data').text || null; 
     alert('Clicked \"' + $(e.target).html() + '\" from the value: ' + data);   
   }
});

Putting together the above, here's a snippet:

$(".form-control").select2({
  templateResult: formatSelect2,
});

var selectInst = $(".form-control").data('select2');

selectInst.$results.on('mousedown', function (e) {
 if($(e.target).closest('.statuses span').length) {
   var data = $(e.target).parents('li[aria-selected]').data('data').text || null; 
   alert('Clicked \"' + $(e.target).html() + '\" from the value: ' + data); 
  }
});

function formatSelect2 (data) {
  if (!data.id) { return data.text; }
  var $data = $(
    '<span data-status="' + data.element.getAttribute("data-status") + '">' + data.text + '<span class="statuses"><span data-status="1">1</span><span data-status="2">2</span><span data-status="3">3</span></span></span>'
  );
  return $data;
}
.statuses {
  margin-left: 8px;
}
.statuses span {
  margin-left: 8px;
  cursor: pointer;
}
.statuses span:hover {
  margin-left: 8px;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.js"></script>
<select class="form-control" style="width: 300px">
  <option selected="selected" data-status="1">orange</option>
  <option>white</option>
  <option>purple</option>
</select>

EDIT:

For multiple instances of select2, here's one approach:

Apply the same above logic to all the instances.

I've added a class render_select2 to all selects for which select2 is going to be initialized. Using that class, here's how the above can be applied for all the instances:

$(".render_select2").each(function () { 
  var selectInst = $(this).data('select2');
  selectInst.$results.on('mousedown', function (e) {
    if($(e.target).closest('.statuses span').length) {
      var data = $(e.target).parents('li[aria-selected]').data('data').text || null; 
      alert('Clicked \"' + $(e.target).html() + '\" from the value: ' + data);  
    }
  });
});

$(".form-control").select2({
  templateResult: formatSelect2,
});

$(".render_select2").each(function () { 
 var selectInst = $(this).data('select2');
 selectInst.$results.on('mousedown', function (e) {
    if($(e.target).closest('.statuses span').length) {
      var data = $(e.target).parents('li[aria-selected]').data('data').text || null; 
      alert('Clicked \"' + $(e.target).html() + '\" from the value: ' + data); 
    }
 });
});

function formatSelect2 (data) {
  if (!data.id) { return data.text; }
  var $data = $(
    '<span data-status="' + data.element.getAttribute("data-status") + '">' + data.text + '<span class="statuses"><span data-status="1">1</span><span data-status="2">2</span><span data-status="3">3</span></span></span>'
  );
  return $data;
}
.statuses {
  margin-left: 8px;
}
.statuses span {
  margin-left: 8px;
  cursor: pointer;
}
.statuses span:hover {
  margin-left: 8px;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.js"></script>
<select class="form-control render_select2" style="width: 300px">
  <option selected="selected" data-status="1">orange</option>
  <option>white</option>
  <option>purple</option>
</select>

<select class="form-control render_select2" style="width: 300px">
  <option selected="selected" data-status="1">orange1</option>
  <option>white1</option>
  <option>purple1</option>
</select>

EDIT 2: Avoiding each and using select2:select event:

Using the in-built event, here' the change:

$(".form-control").on('select2:select', function (evt) {
    var origEvent = evt.params.originalEvent;
    if($(origEvent.target).closest('.statuses span').length) {
      var data = $(origEvent.target).parents('li[aria-selected]').data('data').text || null; 
      alert('Clicked \"' + $(origEvent.target).html() + '\" from the value: ' + data);  
    }
});

$(".form-control").select2({
  templateResult: formatSelect2,
});

$(".form-control").on('select2:select', function (evt) {
 var origEvent = evt.params.originalEvent;
     if($(origEvent.target).closest('.statuses span').length) {
      var data = $(origEvent.target).parents('li[aria-selected]').data('data').text || null; 
      alert('Clicked \"' + $(origEvent.target).html() + '\" from the value: ' + data); 
    }
});

function formatSelect2 (data) {
  if (!data.id) { return data.text; }
  var $data = $(
    '<span data-status="' + data.element.getAttribute("data-status") + '">' + data.text + '<span class="statuses"><span data-status="1">1</span><span data-status="2">2</span><span data-status="3">3</span></span></span>'
  );
  return $data;
}
.statuses {
  margin-left: 8px;
}
.statuses span {
  margin-left: 8px;
  cursor: pointer;
}
.statuses span:hover {
  margin-left: 8px;
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.js"></script>
<select class="form-control render_select2" style="width: 300px">
  <option selected="selected" data-status="1">orange</option>
  <option>white</option>
  <option>purple</option>
</select>

<select class="form-control render_select2" style="width: 300px">
  <option selected="selected" data-status="1">orange1</option>
  <option>white1</option>
  <option>purple1</option>
</select>

Hope this helps (and btw I've used v4.5 for this as the v4.6-rc seems like it's in beta stage)

Shashank
  • 5,570
  • 1
  • 11
  • 17
  • Thank you. This looks great! Two questions. 1) Can I make this work on multiple instances of `.form-control`? Right now it only works on the first instance. 2) I get the following message `TypeError: t(...).parents(...).data(...) is undefined` Any ideas? I'm running `4.0.6-rc.1` Thank you again! – SeaBass Aug 23 '18 at 18:55
  • 1. Yes, it should work for multiple instances, show up a fiddle link that I can take a look at. 2. As I stated in the last line, it seems like v4.6-rc is in development stage which doesn't assign anything to the `data` attribute and so the error. Try using v4.5 (the last stable version) – Shashank Aug 23 '18 at 19:04
  • Changed to 4.0.5 and it works! Just have to try everything out to see that nothing else broke. Thanks! I edited your code above. See how it only works on the first select2? – SeaBass Aug 23 '18 at 19:21
  • Edited the post! Hope that helps. – Shashank Aug 23 '18 at 19:37
  • Fantastic! Everything working :) If you don't mind. Would any of of the built in events be able to be used in lieu of the `each`? I will have a couple of hundred of these, but maybe it doesn't slow it down at all with an each, was just thinking. https://select2.org/programmatic-control/events – SeaBass Aug 23 '18 at 19:55
  • Sure. That's possible as well. I just got too much determined to make it work on a `mousedown`. Editing the post now. – Shashank Aug 23 '18 at 20:07
  • Oh, I'd still need a mouse click, was just thinking if it was included in one of the existing events. There is no `select2:click` in the list, but wondered if you could reach it in `select2:selecting` or something. – SeaBass Aug 23 '18 at 20:10
  • That's brilliant! Bare with me, sorry to be annoying. If you arrow down the menu and hit enter you get `TypeError: origEvent is undefined`. It seems like this only works on `select2:select`. Can it work on `select2:close` or `select2:closing` too? Then it may still be possible to click on the already selected option and get the alert as well. – SeaBass Aug 23 '18 at 20:31
  • True. Keyboards events won't work. How would you navigate to the status's span anyway using keys? Yes, I think those events should export pretty much the same args. Make Chrome console your friend and try adding those events. – Shashank Aug 23 '18 at 21:02
  • Thanks! I did try that, but I'm just not good enough at JS :( It looks like `origEvent` can't be reached if you click on the already selected item. I've tried all events available. I know about the keyboard events but as long as the error message doesn't show up it's fine if you can't select the "sub options". – SeaBass Aug 23 '18 at 21:23
  • Glad I could help! It's better NOT to continue chatting here in the comments. If possible, could you create a new question and add the specific requirements? – Shashank Aug 23 '18 at 21:25
  • Good idea! Here it is! https://stackoverflow.com/questions/51994554/select2-event-that-triggers-on-any-option-selected-or-not-selected – SeaBass Aug 23 '18 at 21:33