33

I'm looking for a way to attach a click-event to a select2-result-item. I've gone ahead and formatted both result and selection via

function format(state) {
    if (!state.id) return state.text; // optgroup
    return state.text + " <i class='info'>link</i>";
}

to add an "info"-icon I'm now trying to attach a simple click-event to the .info-element but can't get this to work:

$('.info').on('click', function(event){
    event.preventDefault();
    alert("CLICK");
    $('#log').text( "clicked" );
});

Any help? Anyone with a similar problem? Is this possible at all?

I prepared a jsFiddle at: http://jsfiddle.net/s2dqh/3/

j0k
  • 22,600
  • 28
  • 79
  • 90
brckmann
  • 645
  • 3
  • 7
  • 14

12 Answers12

39

Because Select2 lib prevent any click events on popover list you can't bind events to .info directly. But you can redefine onSelect method and place there any code you want.

See example: http://jsfiddle.net/f8q2by55/

Update for multiple selects: http://jsfiddle.net/6jaodjzq/

Gareth Oakley
  • 1,020
  • 1
  • 12
  • 34
ant_Ti
  • 2,385
  • 16
  • 14
  • superb, thanks so far; i know it's exceeding my original question, but how would i make the link in .select2-choice inherit the same behavior? (to clarify: .select2-choice is the element which is selected - the jsfiddle shows that the click-event is only working for the dropdown-options, not for the currently selected element) – brckmann Mar 26 '13 at 14:01
  • @ant_Ti - I ran into too many problems with the "dirty hack" (select2-dropdown not closing, etc.), so I went ahead and implemented a workaround for the info-icon of .select2-choice which does what it needs to without interfering with select2's original functionality. Allow me one more question regarding the first solution (overwriting 'onSelect') though: I noticed when making a choice via the return-key, this results in "Uncaught TypeError: Cannot read property 'target' of undefined" - any clues how to catch this last annoyance? – brckmann Apr 01 '13 at 01:59
  • @ant_Ti - Thanks so much for the quick answer (works like a charm!), really learning a lot here! Allow me one more question: How would I apply the modified 'onSelect' to other instances of select2? – brckmann Apr 01 '13 at 21:19
  • I forgot to add that each of the select2-instances have different options. Happy to open another question for that! – brckmann Apr 01 '13 at 21:37
  • @brckmann see my update to answer. A little refactoring to your code – ant_Ti Apr 02 '13 at 15:37
  • I've also had trouble with this solution, after the first time an info is clicked, some things are broken. Especially when used with an x-editable select2. Posting alternative solution. – AaronLS Jun 12 '13 at 21:29
  • I am using knockout+select2 and i have a basic bindingHandler. How do I use the onSelect event? – robasta Nov 14 '13 at 14:19
  • @rob `onSelect` event is library custom event, so i think there should be no changes – ant_Ti Nov 18 '13 at 09:52
14

For versions of Select2 before 4.0.0 (so 3.5.x and below), you should refer to this answer about binding to onSelect.

In newer versions of Select2, events are no longer killed within the results so you can just bind to the mouseup/mousedown events like you normally would. You should avoid binding to the click event because by default browsers will not trigger it.

$(".select2").select2({
  templateResult: function (data) {
    if (data.id == null) {
      return data.text;
    }
    
    var $option = $("<spam></span>");
    var $preview = $("<a target='_blank'> (preview)</a>");
    $preview.prop("href", data.id);
    $preview.on('mouseup', function (evt) {
      // Select2 will remove the dropdown on `mouseup`, which will prevent any `click` events from being triggered
      // So we need to block the propagation of the `mouseup` event
      evt.stopPropagation();
    });
    
    $preview.on('click', function (evt) {
      console.log('the link was clicked');
    });
    
    $option.text(data.text);
    $option.append($preview);
    
    return $option;
  }
});
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script>

<select class="select2" style="width: 200px;">
  <option value="https://google.com">Google</option>
  <option value="https://mail.google.com">GMail</option>
</select>

In this example we stop the propagation of the mouseup event so the browser will trigger the click event. If you are not working with actual links, but instead need to just catch a click event, you should just hook into the mouseup event.

Community
  • 1
  • 1
Kevin Brown-Silva
  • 40,873
  • 40
  • 203
  • 237
  • I'm using Select 4.0.13 and this worked for me! Select2 fires no events when clicking on an option that is already selected and this was becoming a major headache. Capturing the mouseup event in the templateResult does consistently fire on all option clicks and stopPropagation works here too. – Tanoro Jan 10 '23 at 04:00
11

This is similar in concept to netme's but the select2 event is wrong(maybe version difference), and you need to use stopPropagation to prevent item from being selected:

http://jsfiddle.net/ZhfMg/

$('#mySelect2').on('select2-open', function() { 
    $('.select2-results .info').on('mouseup', function(e) { 
        e.stopPropagation();
        console.log('clicked');
    }); 
});

If used with x-editable. What this does is when the x-editable goes into edit mode(shown event), that's when a select2 exists and you can wire to it's open function.

$('#myXEditable').on('shown', function () {
    $(this).data('editable').input.$input.on('select2-open', function() { 
        $('.select2-results .info').on('mouseup', function(e) { 
            e.stopPropagation();
            console.log('clicked');
        }); 
    });
});
AaronLS
  • 37,329
  • 20
  • 143
  • 202
7

use default select2select event trigger instead of using other jquery events

$('#mySelect2').on('select2:select', function (e) {
    var data = e.params.data;
    console.log(data);
});

for more details refer below link https://select2.org/programmatic-control/events

Balakrishnan Bsk
  • 474
  • 2
  • 5
  • 17
6

None of the answers above worked for me for select2 4.0.0, so here's what I ended up doing:

$(document).on('mouseup', '.select2-container--open .select2-results__option', function (e) {
    // Act on the element
}

Specifically, I wanted to only act on the previously-selected item:

$(document).on('mouseup', '.select2-container--open .select2-results__option[aria-selected=true]', function (e) {
    // Do something on the previously-selected item
}
Aegix
  • 629
  • 6
  • 14
  • 5
    If you can please provide JSfiddle or something to prove your approach is working. It is just too many answers here that are not working for new version of select2. – Yevgeniy Afanasyev Jan 05 '16 at 21:45
6

A simpler approach according to docs.

$('#mySelect2').on('select2:select', function (e) {
    var data = e.params.data;
    console.log(data);
});

Surprised not to see any answer for this.

HalfWebDev
  • 7,022
  • 12
  • 65
  • 103
2

Just add 'open' listener and set up another 'mouseup' listener for the '' tag:

$("#select").on('open', function() { 
    $('.select2-results i').on('mouseup', function() { 
       alert('aaa');
    }); 
});

Here is the link to my solution: http://jsfiddle.net/EW8t7/

Mikhail Chernykh
  • 2,659
  • 22
  • 15
  • 1
    Unfortunately with this answer, clicking the link causes the item to be selected and dropdown closed. Doesn't have the prevent default capability. – AaronLS Jun 10 '13 at 23:12
2

I think Select2's handling of the mouseup event prevents the .info element from getting a click event. Having the .info element capture the mouseup event and prevent it from propagating seems to fix that.

function format(state, container) {
    if (!state.id) return state.text; // optgroup
    container.append(state.text);
    $('<i class="info">link</i>')
        .appendTo(container)
        .mouseup(function(e) {
            e.stopPropagation();            
        })
        .click(function(e) {
            e.preventDefault();
            alert('CLICK');
        });
}

This can also be done to support a link in the selected item, but there it is the mousedown event that needs to be captured.

jsfiddle

John S
  • 21,212
  • 8
  • 46
  • 56
  • For me the `mousedown` event worked (like in your fiddle's `formatSelection` function) and not `mouseup` – Pascal Luxain Feb 13 '15 at 13:52
  • @yezahel - Thanks. My jsfiddle is using v3.3.1, which was the version used in the question. The latest version is 3.5.2, and, unfortunately, I think you are right that the code to make this work needs to be a bit different for that version. – John S Feb 13 '15 at 18:55
2

Just ran into this problem with select2 v4.0.3 and ended up using the 'select2:selecting' event. Here's essentially how I did it:

var $select2 = $savedReportsList.select2({ [stuff] });

$select2.on('select2:selecting', function(e) {
  var args = e.params.args;
  if ($(args.originalEvent.target).hasClass('[something]')) {
    if (confirm('Are you sure?')) {
       window.location = '[somewhere]' + '/' + args.data.id;
    }
    return false; // stops propogation (so select2:select never fires)
  });
});
etipton
  • 164
  • 1
  • 5
1

the thing seems to be that the tgas will be deleted right after the box is closed. Therefore the click event cannot be fired.

I've changed a few things + the event that will be listened on. It's now a mousedown...

function format(state) {
    if (!state.id) return state.text; // optgroup
    return state.text + " <a class='info'>link</a>";
}

$("select").select2({
    formatResult: format,
    formatSelection: format,
    escapeMarkup: function(m) { return m; }
}).on('select2-open',function(){
    console.log($('.info'))
    $('.info').on('mousedown', function(event){
        console.log("click")
        $('#log').text( "clicked" );
        event.preventDefault();
    });
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.2/select2.js"></script>

<select>
  <option>red</option>
  <option>green</option>
  <option>blue</option>
</select>

<div id="log"></div>
Kevin Brown-Silva
  • 40,873
  • 40
  • 203
  • 237
Christian
  • 6,961
  • 10
  • 54
  • 82
  • Good idea but in your case you won't be able to prevent closing list by clicking on `.info` – ant_Ti Mar 26 '13 at 13:05
0

You need to unbind the mouseup event from the .select2-results__options element. I'm doing it in my templateResult function:

function templateResult(location) {

    var $location = $([html with links]);

    $('.select2-results__options').unbind('mouseup');

    return $location;
}

If you want to keep select2's default behavior of selecting the option and closing the dropdown, you'll need manually trigger those events:

$('.select2-results__options').unbind('mouseup');

$('a', $location).on('click', function (e) {
    $('#locations_dropdown').select2('val', location.id);
    $('#locations_dropdown').select2('close');
});

return $location;
divups
  • 114
  • 9
  • This will only work for standard selects, where the ` – Kevin Brown-Silva Aug 09 '15 at 13:35
0

Simple onchange event like this also would help to solve the problem

<select id="GetClientAccounts" class="default-select2 form-control col-md-4" onchange="GetClientAccounts();">
    <option disabled selected value="-1">Select a customer from here</option>
    @{
        if (ViewBag.customers != null)
        {
            foreach (CustomerTBL customer in ((List<CustomerTBL>)ViewBag.customers).OrderBy(c => c.cusCustomerName))
            {
                <option value="@customer.cusCustomerID">@customer.cusCustomerName</option>
            }
        }
    }
</select>
pistou
  • 2,799
  • 5
  • 33
  • 60
PraBel
  • 1
  • 1