Jason
Jason

Reputation: 52523

How do you detect the clearing of a "search" HTML5 input?

In HTML5, the search input type appears with a little X on the right that will clear the textbox (at least in Chrome, maybe others). Is there a way to detect when this X is clicked in Javascript or jQuery other than, say, detecting when the box is clicked at all or doing some sort of location click-detecting (x-position/y-position)?

Upvotes: 252

Views: 184371

Answers (28)

maxshuty
maxshuty

Reputation: 10662

Modern easy & readable solution

Wow, there are some really complicated answers in here for a really simple problem.

Simply add a listener for 'input' on your search input which will capture when the user types something in the input or clicks on the clear icon.

document.getElementById('searchInput').addEventListener('input', (e) => {
  console.log(`Input value: "${e.currentTarget.value}"`);
})
<input id="searchInput" type="search" placeholder="Search" />

If you can't use ES6+ then here is the converted code for you:

document.getElementById('searchInput').addEventListener('input', function(e) { 
  // do stuff
})` 

If you need to perform some action only when the user clicks the X, but not when they delete their text, see this comment from @GlenVaughn:

...this solution alone won't solve the problem. In my case, checking for if (!e.currentTarget.value && !e.inputType) worked. (Noticed in DevTools that the event object always includes an inputType value when the text is deleted, but not when the X is clicked.)

Upvotes: 40

amos
amos

Reputation: 51

Hi, i needed to get the clear button event and store the last search string. tried all the answers and came up with this one in the INPUT i added onInput={handleInput} i am working with react so i am remembering the value onChange in useState. when onChange triggers it update the state VALUE and state VALUECOPY (2 states) when "onInput" fires: i check if the input is empty then i take the last updated value from the copy and reset the copy! hope it helped

const handleInput = (event: React.FormEvent<HTMLInputElement>) => {
    const { value } = event?.currentTarget;
    if (value === '') {
      console.log('last search string:',inputValueCopy);
      setInputValueCopy('')// reset copy state
    }
  };

Upvotes: 0

Danial
Danial

Reputation: 1614

some browsers have a search event, but Safari for one, does not fire it. It does fire an input event, however. I've been catching it like this for years:

$('foo').on ('input', function(e){
    let type = e.inputType || e.originalEvent.inputType;
    if (typeof(type) == 'undefined'){
        alert("Got Search");
    }
});

It may not be sound but I've been using it for years. Chrome will fire both an undefined input type and also a search, so this method works with both Safari and Chrome. Firefox doesn't even put the X in the field so it's moot with FF. The only event types you get are insert, delete search and undefined; at least I've never seen anything else

Upvotes: 0

Pudica
Pudica

Reputation: 188

The selected answer uses the onsearch event, which is non-standard, but I'll go with that. I'm also going to reverse the question: "detect the clearing of a search" becomes "detect when Enter has been pressed". This will be fine if you want to shift the focus to the search results when the user presses Enter, for example, but remain in the search field when they press Escape or the X button.

Event listeners are fired in the order they are registered, so you can register a second search listener within a keydown event, and it will fire after the search has been done. The second search listener can do the things you want to do when the user intentionally performs a search.

input.addEventListener("search", () => console.log("doing the search"));

input.addEventListener("keydown", function() {
  if (event.code === "Enter")
    this.addEventListener(
      "search",
      () => console.log("focus() the results"),
      {once: true})
});

The key to this trick is the "once" option in the second search listener. It removes the listener when it has run one time, so subsequent searches are not affected.

Upvotes: 0

Nikkorian
Nikkorian

Reputation: 790

I might as well add my 5c worth.

The keyup event does not detect the mouse click on the X to clear the field, but the input event detects both the keystroke and the mouse click. You can distinguish between the events that trigger the input event by examining the event's originalEvent property - there are quite a few differences.

I found the simplest way is as follows:

jQuery("#searchinput").on("input",function(event) {
      var isclick = event.originalEvent.inputType == undefined;
   }   

With a keystroke, the event.originalEvent.inputType = "insertText".

I am using Chrome - not tested in other browsers but given the event object is pretty universal, my guess is this will work in most contexts.

Note that just clicking into the input does not trigger the event.

Upvotes: 0

rubeonline
rubeonline

Reputation: 1298

You can give fuse.js a try. In the basic configuration it could be possible that to many results where filtered by the search. But you have in the options object of the fuse instance a lot of possibilities to adjust your search: https://fusejs.io/api/options.html

For example: The treshold is set by default to 0.6. If you go higher up to 1 it does not filter anything. If you go down to 0 only exact matches are in the result. Play around with these settings.

Its a client side search which is implemented in minutes and imho sufficient in a lots of use cases.

Upvotes: 0

Damien Golding
Damien Golding

Reputation: 1000

At least in Chrome, it seems that the "X" button of the search input emits a different kind of event.

It is also stated on MDN that an InputEvent or Event can be emitted: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event

Test below. You will see that text inputs will be an InputEvent with a "data" property containing the character inputted, and that X button clicks will emit an Event type.

document.querySelector('input[type=search]').addEventListener('input', ev => console.log(ev))

Therefore, it should be possible to distinguish using:

if (ev instanceof InputEvent) { ... }

Upvotes: 2

Yogesh
Yogesh

Reputation: 725

You can also handle with generic way by binding onInput event as below

<input type="search" oninput="myFunction()">

Upvotes: 1

GregP
GregP

Reputation: 1624

In my case I didn't want to use JQuery and my input was also generic so in some instances it could be of type 'search' but not always. I was able to get it working with a little delay based off one of the other answers in here. Basically I wanted to open a component when the input was clicked but not if the clear button was clicked.

function onClick(e: React.MouseEvent<HTMLInputElement>) {
  const target = e.currentTarget;
  const oldValue = target.value;
  setTimeout(() => {
    const newValue = target.value;
    if (oldValue && !newValue) {
      // Clear was clicked so do something here on clear
      return;
    }

    // Was a regular click so do something here
  }, 50);
};

Upvotes: 0

Pauan
Pauan

Reputation: 2663

Actually, there is a "search" event that is fired whenever the user searches, or when the user clicks the "x". This is especially useful because it understands the "incremental" attribute.

Now, having said that, I'm not sure if you can tell the difference between clicking the "x" and searching, unless you use an "onclick" hack. Either way, hopefully this helps.

Dottoro web reference

Upvotes: 149

Lorenzo Dematt&#233;
Lorenzo Dematt&#233;

Reputation: 7839

I want to add a "late" answer, because I struggled with change, keyup and search today, and maybe what I found in the end may be useful for others too. Basically, I have a search-as-type panel, and I just wanted to react properly to the press of the little X (under Chrome and Opera, FF does not implement it), and clear a content pane as a result.

I had this code:

 $(some-input).keyup(function() { 
    // update panel
 });

 $(some-input).change(function() { 
    // update panel
 });

 $(some-input).on("search", function() { 
    // update panel
 });

(They are separate because I wanted to check when and under which circumstances each was called).

It turns out that Chrome and Firefox react differently. In particular, Firefox treats change as "every change to the input", while Chrome treats it as "when focus is lost AND the content is changed". So, on Chrome the "update panel" function was called once, on FF twice for every keystroke (one in keyup, one in change)

Additionally, clearing the field with the small X (which is not present under FF) fired the search event under Chrome: no keyup, no change.

The conclusion? Use input instead:

 $(some-input).on("input", function() { 
    // update panel
 }

It works with the same behaviour under all the browsers I tested, reacting at every change in the input content (copy-paste with the mouse, autocompletion and "X" included).

Upvotes: 114

mad4power
mad4power

Reputation: 15

Looks like there isn't a good answer to this so I thought I would add another possible solution.

// Get the width of the input search field
const inputWidth = $event.path[0].clientWidth;
// If the input has content and the click is within 17px of the end of the search you must have clicked the cross
if ($event.target.value.length && ($event.offsetX < inputWidth && $event.offsetX > inputWidth - 17)) {
    this.tableRows = [...this.temp_rows];
}

Update

const searchElement = document.querySelector('.searchField');
searchElement.addEventListener('click', event => {
  // Get the width of the input search field
  const inputWidth = $event.path[0].clientWidth;
  // If the input has content and the click is within 17px of the end of the search you must have clicked the cross
  if ($event.target.value.length && ($event.offsetX < inputWidth && $event.offsetX > inputWidth - 17)) {
    this.tableRows = [...this.temp_rows];
}
});

Upvotes: 0

marcodb
marcodb

Reputation: 91

The original question is "Can I detect a click of the 'x'?". This can be achieved by "sacrificing" Enter in the search event.

There are many events firing at different times in the lifecycle of an input box of type search: input, change, search. Some of them overlap under certain circumstances. By default, "search" fires when you press Enter and when you press the 'x'; with the incremental attribute, it also fires when you add/remove any character, with a 500ms delay to capture multiple changes and avoid overwhelming the listener. The trouble is, search generates an ambiguous event with input.value == "", because there are three ways it could have turned empty: (1) "the user pressed the 'x'", (2) "the user pressed Enter on an input with no text", or (3) "the user edited the input (Backspace, cut, etc) till the input became empty, and eventually incremental triggered the search event for the empty input".

The best way to disambiguate is to take Enter out of the equation, and have search fire only when the 'x' is pressed. You achieve that by suppressing the Enter keypress altogether. I know it sounds silly, but you can get the Enter behavior back under better controlled circumstances via the keydown event (where you'd do the suppressing too), the input event or the change event. The only thing that is unique to search is the 'x' click.

This removes the ambiguity if you don't use incremental. If you use incremental, the thing is, you can achieve most of the incremental behavior with the input event (you'd just need to re-implement the 500ms debouncing logic). So, if you can drop incremental (or optionally simulate it with input), this question is answered by a combination of search and keydown with event.preventDefault(). If you can't drop incremental, you'll continue to have some of the ambiguity described above.

Here's a code snippet demonstrating this:

inpEl = document.getElementById("inp");
monitor = document.getElementById("monitor");

function print(msg) {
  monitor.value += msg + "\n";
}

function searchEventCb(ev) {
  print(`You clicked the 'x'. Input value: "${ev.target.value}"`);
}

function keydownEventCb(ev) {
    if(ev.key == "Enter") {
    print(`Enter pressed, input value: "${ev.target.value}"`);
        ev.preventDefault();
    }
}

inpEl.addEventListener("search", searchEventCb, true);
inpEl.addEventListener("keydown", keydownEventCb, true);
<input type="search" id="inp" placeholder="Type something">

<textarea id="monitor" rows="10" cols="50">
</textarea>

In this simple snippet, you've turned search into a dedicated event that fires only when you press 'x', and that answers the question originally posted. You track input.value with the keydown for Enter.

Personally, I prefer to put an ev.target.blur() when pressing the Enter key (simulating a loss of focus for the input box), and monitor the change event to track the input.value (instead of monitoring input.value via keydown). In this way you can uniformly track input.value on focus changes, which can be useful. It works for me because I need to process the event only if the input.value has actually changed, but it might not work for everybody.

Here's the snippet with the blur() behavior (now you'll get a message even if you manually focus away from the input box, but remember, expect to see a change message only if a change actually happened):

inpEl = document.getElementById("inp");
monitor = document.getElementById("monitor");

function print(msg) {
  monitor.value += msg + "\n";
}

function searchEventCb(ev) {
  print(`You clicked the 'x'. Input value: "${ev.target.value}"`);
}

function changeEventCb(ev) {
  print(`Change fired, input value: "${ev.target.value}"`);
}

function keydownEventCb(ev) {
    if(ev.key == "Enter") {
        ev.target.blur();
        ev.preventDefault();
    }
}

inpEl.addEventListener("search", searchEventCb, true);
inpEl.addEventListener("change", changeEventCb, true);
inpEl.addEventListener("keydown", keydownEventCb, true);
<input type="search" id="inp" placeholder="Type something">

<textarea id="monitor" rows="10" cols="50">
</textarea>

Upvotes: 3

Damarawy
Damarawy

Reputation: 21

My solution is based on the onclick event, where I check the value of the input (make sure that it's not empty) on the exact time the event fires and then wait for 1 millisecond and check the value again; if it's empty then it means that the clear button have been clicked not just the input field.

Here's an example using a Vue function:

HTML

<input
  id="searchBar"
  class="form-input col-span-4"
  type="search"
  placeholder="Search..."
  @click="clearFilter($event)"
/>

JS

clearFilter: function ($event) {
  if (event.target.value !== "") {
    setTimeout(function () {
      if (document.getElementById("searchBar").value === "")
        console.log("Clear button is clicked!");
    }, 1);
  }
  console.log("Search bar is clicked but not the clear button.");
},

Upvotes: 0

Cory Gagliardi
Cory Gagliardi

Reputation: 780

It made sense to me that clicking the X should count as a change event. I already had the onChange event all setup to do what I needed it to do. So for me, the fix was to simply do this jQuery line:

$('#search').click(function(){ $(this).change(); });

Upvotes: 6

realmag777
realmag777

Reputation: 2088

document.querySelectorAll('input[type=search]').forEach(function (input) {
   input.addEventListener('mouseup', function (e) {
                if (input.value.length > 0) {
                    setTimeout(function () {
                        if (input.value.length === 0) {
                            //do reset action here
                        }
                    }, 5);
                }
            });
}

ECMASCRIPT 2016

Upvotes: 1

Revanth
Revanth

Reputation: 273

Here's one way of achieving this. You need to add incremental attribute to your html or it won't work.

window.onload = function() {
  var tf = document.getElementById('textField');
  var button = document.getElementById('b');
  button.disabled = true;
  var onKeyChange = function textChange() {
    button.disabled = (tf.value === "") ? true : false;
  }
  tf.addEventListener('keyup', onKeyChange);
  tf.addEventListener('search', onKeyChange);

}
<input id="textField" type="search" placeholder="search" incremental="incremental">
<button id="b">Go!</button>

Upvotes: 2

James Yang
James Yang

Reputation: 1416

based on event-loop of js, the click on clear button will trigger search event on input, so below code will work as expected:

input.onclick = function(e){
  this._cleared = true
  setTimeout(()=>{
    this._cleared = false
  })
}
input.onsearch = function(e){
  if(this._cleared) {
    console.log('clear button clicked!')
  }
}

The above code, onclick event booked a this._cleared = false event loop, but the event will always run after the onsearch event, so you can stably check the this._cleared status to determine whether user just clicked on X button and then triggered a onsearch event.

This can work on almost all conditions, pasted text, has incremental attribute, ENTER/ESC key press etc.

Upvotes: 1

lostInTheTetons
lostInTheTetons

Reputation: 1222

I know this is an old question, but I was looking for the similar thing. Determine when the 'X' was clicked to clear the search box. None of the answers here helped me at all. One was close but also affected when the user hit the 'enter' button, it would fire the same result as clicking the 'X'.

I found this answer on another post and it works perfect for me and only fires when the user clears the search box.

$("input").bind("mouseup", function(e){
   var $input = $(this),
   oldValue = $input.val();

   if (oldValue == "") return;

   // When this event is fired after clicking on the clear button
   // the value is not cleared yet. We have to wait for it.
   setTimeout(function(){
     var newValue = $input.val();

      if (newValue == ""){
         // capture the clear
         $input.trigger("cleared");
      }
    }, 1);
});

Upvotes: 9

priyaranjan sahoo
priyaranjan sahoo

Reputation: 1

On click of TextField cross button(X) onmousemove() gets fired, we can use this event to call any function.

<input type="search" class="actInput" id="ruleContact" onkeyup="ruleAdvanceSearch()" placeholder="Search..." onmousemove="ruleAdvanceSearch()"/>

Upvotes: -3

Tarandeep Singh
Tarandeep Singh

Reputation: 1390

Full Solution is here

This will clear search when search x is clicked. or will call the search api hit when user hit enter. this code can be further extended with additional esc keyup event matcher. but this should do it all.

document.getElementById("userSearch").addEventListener("search", 
function(event){
  if(event.type === "search"){
    if(event.currentTarget.value !== ""){
      hitSearchAjax(event.currentTarget.value);
    }else {
      clearSearchData();  
    }
  }
});

Cheers.

Upvotes: 3

dlsso
dlsso

Reputation: 8071

I believe this is the only answer that fires ONLY when the x is clicked.

However, it is a bit hacky and ggutenberg's answer will work for most people.

$('#search-field').on('click', function(){
  $('#search-field').on('search', function(){
    if(!this.value){
      console.log("clicked x");
      // Put code you want to run on clear here
    }
  });
  setTimeout(function() {
    $('#search-field').off('search');
  }, 1);
});

Where '#search-field' is the jQuery selector for your input. Use 'input[type=search]' to select all search inputs. Works by checking for a search event (Pauan's answer) immediately after a click on the field.

Upvotes: 2

Don Zanurano
Don Zanurano

Reputation: 15

try this, hope help you

$("input[name=search-mini]").on("search", function() {
  //do something for search
});

Upvotes: 1

Richard Dufour
Richard Dufour

Reputation: 71

The search or onclick works... but the issue I found was with the older browsers - the search fails. Lots of plugins (jquery ui autocomplete or fancytree filter) have blur and focus handlers. Adding this to an autocomplete input box worked for me(used this.value == "" because it was faster to evaluate). The blur then focus kept the cursor in the box when you hit the little 'x'.

The PropertyChange and input worked for both IE 10 and IE 8 as well as other browsers:

$("#INPUTID").on("propertychange input", function(e) { 
    if (this.value == "") $(this).blur().focus(); 
});

For FancyTree filter extension, you can use a reset button and force it's click event as follows:

var TheFancyTree = $("#FancyTreeID").fancytree("getTree");

$("input[name=FT_FilterINPUT]").on("propertychange input", function (e) {
    var n,
    leavesOnly = false,
    match = $(this).val();
    // check for the escape key or empty filter
    if (e && e.which === $.ui.keyCode.ESCAPE || $.trim(match) === "") {
        $("button#btnResetSearch").click();
        return;
    }

    n = SiteNavTree.filterNodes(function (node) {
        return MatchContainsAll(CleanDiacriticsString(node.title.toLowerCase()), match);
        }, leavesOnly);

    $("button#btnResetSearch").attr("disabled", false);
    $("span#SiteNavMatches").text("(" + n + " matches)");
}).focus();

// handle the reset and check for empty filter field... 
// set the value to trigger the change
$("button#btnResetSearch").click(function (e) {
    if ($("input[name=FT_FilterINPUT]").val() != "")
        $("input[name=FT_FilterINPUT]").val("");
    $("span#SiteNavMatches").text("");
    SiteNavTree.clearFilter();
}).attr("disabled", true);

Should be able to adapt this for most uses.

Upvotes: 1

Sharad Biradar
Sharad Biradar

Reputation: 2074

Bind search-event the search box as given below-

$('input[type=search]').on('search', function () {
    // search logic here
    // this function will be executed on click of X (clear button)
});

Upvotes: 104

ScottG
ScottG

Reputation: 11

Found this post and I realize it's a bit old, but I think I might have an answer. This handles the click on the cross, backspacing and hitting the ESC key. I am sure it could probably be written better - I'm still relatively new to javascript. Here is what I ended up doing - I am using jQuery (v1.6.4):

var searchVal = ""; //create a global var to capture the value in the search box, for comparison later
$(document).ready(function() {
  $("input[type=search]").keyup(function(e) {
    if (e.which == 27) {  // catch ESC key and clear input
      $(this).val('');
    }
    if (($(this).val() === "" && searchVal != "") || e.which == 27) {
      // do something
      searchVal = "";
    }
    searchVal = $(this).val();
  });
  $("input[type=search]").click(function() {
    if ($(this).val() != filterVal) {
      // do something
      searchVal = "";
    }
  });
});

Upvotes: 1

ggutenberg
ggutenberg

Reputation: 7360

Using Pauan's response, it's mostly possible. Ex.

<head>
    <script type="text/javascript">
        function OnSearch(input) {
            if(input.value == "") {
                alert("You either clicked the X or you searched for nothing.");
            }
            else {
                alert("You searched for " + input.value);
            }
        }
    </script>
</head>
<body>
    Please specify the text you want to find and press ENTER!
    <input type="search" name="search" onsearch="OnSearch(this)"/>
</body>

Upvotes: 20

mVChr
mVChr

Reputation: 50177

It doesn't seem like you can access this in browser. The search input is a Webkit HTML wrapper for the Cocoa NSSearchField. The cancel button seems to be contained within the browser client code with no external reference available from the wrapper.

Sources:

Looks like you'll have to figure it out through mouse position on click with something like:

$('input[type=search]').bind('click', function(e) {
  var $earch = $(this);
  var offset = $earch.offset();

  if (e.pageX > offset.left + $earch.width() - 16) { // X button 16px wide?
    // your code here
  }
});

Upvotes: 4

Related Questions