Client side search on text change - when to send request? - javascript

I have a search engine for an entity, and it has many filters.
I am required to update the search results when the filters change.
Some filters are selected from dropdowns, others are text inputs.
What is the correct way to implement search on change, when the change can happen frequently as the user types?
Here are some possibilities:
When the input loses focus. The problem: the user might type away some text and wait for the search to work, but it won't.
On each keystroke. The problem is I'll flood the server with requests.
Wait x milliseconds after the last change in the input and then send the request. The problem is the search might become slow for the user.
What is the best approach?

You can either do keydown or keyup for inputs. If it's a dropdown menu you can use onchange.
Personally, I would recommend adding a minimum character limit so you're not hammering your resources.
For example
var foo = document.querySelector("#inputElement");
// Change your event listener to what you need.
foo.addEventListener("keyup", doSomeStuff);
function doSomeStuff(e){
var input = e.value;
// Rest of your function
}
You can learn about event listeners here

Related

Android Espresso WebView webClick() and webKeys() does not trigger blur event on input field

I'm trying to test a WebView using Espresso. The WebView contains some input fields and a button to continue to the next page. I am able to enter the text in the input fields. After leaving the input field a blur event should get called that performs some additional formatting and validation. However the blur event never gets called and therefore I cannot continue to the next page.
It looks like webKeys() is just injecting some text and that webClick() simply triggers a click event. That's probably why focus never changes and the blur event never gets called. When I physically press the input field myself the event does get triggered and I also see the Android input cursor. When the test case is running I don't see the cursor when text is entered in the input fields.
Is there a way to trigger the blur event programmatically or set focus to an element within the WebView using Espresso?
This is the code I'm using for inserting text.
public static void insertText(String label, String content)
{
onWebView().withElement(findElement(Locator.XPATH, "//div[preceding::span[.='" + label + "']]/input")).perform(clearElement()).perform(webClick())
.perform(webKeys(content));
}
Edit: Added relevant dependencies.
implementation "androidx.test.espresso:espresso-idling-resource:3.2.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"
androidTestImplementation "androidx.test.espresso:espresso-intents:3.2.0"
androidTestImplementation "androidx.test.espresso:espresso-web:3.2.0"
androidTestImplementation "androidx.test.espresso:espresso-contrib:3.2.0"
You can send any javascript to your Web view.
Every "atom" (webKeys is one of atoms) is just some javascript code, which is sent to your Web View. webKeys atom probably sends keypress events, which does not fire focus/blur/change events in browsers. This is sort of standard browsers behaviour for userspace code dispatched events.
As you can manually trigger keypress, you can trigger focus too etc. You can create and dispatch focus by using:
onWebView().withElement(...)
.perform(SimpleAtom("function(elem) {\n" +
"var e = document.createEvent('Event');\n" +
"e.initEvent('focus', true, true);\n" +
"elem.dispatchEvent(e);}"
))
This should set focus to given element. You are sending function, which is then invocated with yours element as first argument.
You should create some classes like SetFocusAtom or RemoveFocusAtom etc. which will extend SimpleAtom instead of copy&paste whole javascript everywhere.
Be aware:
You can send any javascript to WebView, but you probably don't want. You're free to edit values of currently invisible inputs etc. You're writing UI test, so you could test only visible parts of UI. Original atoms (webClick, webKeys etc.) are checking items for actual visibility. You should everytime use some of original atom or at least match for visibility.
You can do only what is possible in userspace Javascript. There are some limitations. Like current question for example. In projects with complex WebViews, you should consider using UIAutomator or some implementation of Selenium (using chromedriver). Espresso-web is not "acting as real user would". This is not achievable by javascript injections only. Chromedriver is much powerfull in user-like acting.
My answer is not generic. Sometimes you don't need to set focus/blur on inputs. Sometimes you want to send change event for example, which is also not triggered by webKeys.

Always return focus to a specific input field

I do not think this exact question has an answer anywhere else.
I have a page with a couple of input fields, and lots of ajax activity for various things. It is an online POS interface. When any of the ajax actions are performed, ususally by a button click, it is desired to always return focus to the main barcode input field.
Whilst this can be done by always calling $('#fieldid').focus() after any of these actions, I was wondering if there was a way of specifying that this field should always have focus after any action. There are other input fields where you need to type a name or something so obviously it cannot just set a timer and set focus on timeout as you would need to allow time for typing.
One option would be to have a timer always running to do it, and constantly reset it when typing. This seems to perhaps be sensible as these fields do have live search on typing associated with them.
Maybe I have answered my own question here, but any better way of doing it would bt greatly appreciated.
Have you tried using the focus trigger within your callback, after appending the html?
Example:
$.post(
'ajax.php',
{ next:next },
function(data){
$('body').append($(data).hide().fadeIn('slow'));
$('textarea').focus();
}
);
To avoid repeated code for each ajax call,
use ajaxStop event.
$.ajaxStop(function(){
$('textarea').focus();
});
You can use preventDefault(); function in Javascript

how to duplicate the value of one form field in another in jquery

I have a form where a user can chose the applications he has access to? and when he the applications he wants access to, he is presented with more options. One of them is notification email address. if the user choses two applications, he gets two notification email address fields to fill. But both of them are stored in the same column in the database.
There are multiple fields like user authorisation level, user branch etc which are uniform accross all applications.
i am trying to replicate the value of one field in the other with jquery. i tried the below which obviously fails. Can anyone suggest how to acheive the replication?
$("#email").val().change(function() {
$("#email1").val($("#email").val());
});
Edit: I have drop down fields as well as text boxes.
.val() returns a string which cannot have a change event. You need to use
$("#email").change(function() {
$("#email1").val($(this).val());
});
You will want to bind the change event using on or live depending on your version of jquery, if you haven't wrapped this piece of code in a ready block.:
$("#email").on("change",function() {
$("#email1").val($(this).val());
});
This fiddle shows setting a <select> tags value using .val() http://jsfiddle.net/8UG9x/
It is an often asked question:
Copy another textbox value in real time Jquery
depending on when you need this action to execute, but if live
$(document).ready(function() {
$('#proname').live('keyup',function() {
var pronameval = $(this).val();
$('#provarname').val(pronameval.replace(/ /g, '-').toLowerCase());
});
});
http://jsfiddle.net/KBanC/
another one, basically same:
JQUERY copy contents of a textbox to a field while typing

Emulating jquery ui autocomplete to search and populate a div

I am using jQuery on method to do a search and populate my div with the search results. I have an input text box which is part of the div's contents. I am using the keyup event to read the text entered and then using ajax to call my server and do the search. When I return the results I populate the text box with the text that has been typed so far. The rest of the content I return is the search results. This is all working fine.
What I want to however is have the focus be placed in my input text box and the cursor placed after the last character typed so that the user can just keep typing and the search results will keep changing with every keystroke. I imagine there is some way to do this with jQuery and JavaScript but I don't know how.
Edit:
Maybe I wasn't clear, but I found the answer at this question: jQuery - Place cursor in input field when link clicked.
Basically all I needed to do was:
var searchBox = $("#search");
searchBox.focus();
searchBox[0].selectionStart = searchBox[0].selectionEnd = searchBox.val().length;
I think you can use select2 plugin. Take a look here, maybe it can help you: http://ivaynberg.github.io/select2/
If I understand correctly, you need to do less rather than more.
You say, "when I return the results I populate the text box with the text that has been typed so far".
Why? The text that has been typed so far is already in the text box, so it should not need to be replaced. By not replacing the text, the user will be able to carry on typing.
There's an additional aspect that you may not have considered ...
The user will almost undoubtedly be able to out-type the ajax responses, so you should take measures to abort an unfulfilled ajax request before issuing a new one. This is made simple by the jQuery jqXHR object returned by $.ajax() (and its shorthand versions) in the form of an abort() method.
Failure to abort could result in the wrong set of search results being displayed against the current text, as the ajax responses are not guaranteed to arrive back in the same order their requests were issued.

Trigger onBlur on multiple elements as a single unit

I have two inputs that together form a single semantic unit (think an hours and minutes input together forming a time input). If both inputs lose focus I want to call some Javascript function, but if the user merely jumps between those two, I don't want to trigger anything.
I've tried wrapping these two inputs in a div and adding an onBlur to the div, but it never triggers.
Next I tried adding onBlurs to both inputs and having them check the other's :focus attribute through jQuery, but it seems that when the onBlur triggers the next element hasn't received focus yet.
Any suggestions on how to achieve this?
EDIT: Someone questioned the purpose of this. I'd like to update a few other fields based on the values contained by both these inputs, but ideally I don't want to update the other fields if the user is still in the process of updating the second input (for instance if the user tabs from first to second input).
I made a working example here:
https://jsfiddle.net/bs38V/5/
It uses this:
$('#t1, #t2').blur(function(){
setTimeout(function(){
if(!$('#t1, #t2').is(':focus')){
alert('all good');
}
},10);
});
var focus = 0;
$(inputs).focus(function() { focus++ });
$(inputs).blur(function() {
focus--;
setTimeout(function() {
if (!focus) {
// both lost focus
}
}, 50);
});
An alternative approach is to check the relatedTarget of the blur event. As stated in the MDN documentation this will be the element which is receiving the focus (if there is one). You can handle the blur event and check if the focus has now been put in your other input. I used a data- attribute to identify them, but you could equally well use the id or some other information if it fits your situation better.
My code is from an angular project I've worked on, but the principle should translate to vanilla JS/other frameworks.
<input id="t1" data-customProperty="true" (blur)="onBlur($event)">
<input id="t2" data-customProperty="true" (blur)="onBlur($event)">
onBlur(e: FocusEvent){
const semanticUnitStillHasFocus = (val.relatedTarget as any)?.dataset?.customProperty === "true";
// Do whatever you like with this knowledge
}
What is the purpose of this behavior ?
The blur event triggers when a field looses focus, and only one field can gain focus at a time.
What you could do, in case of validation for instance, is to apply the same function on blur for both the fields and check the values of the fields altogether.
Without a context, it is difficult to help you more.
d.

Categories

Resources