Require JS Expert to help with a debounce function

Hello all

Framework v15.32

I’m trying to code a debounce function on a Web Form, not a DocType Form View. Although I think the code will be transferable to a Form View. The debounce is needed to allow the user sufficient time to enter a string into a Data fieldtype before the validation is triggered.

The code below executes fine when I apply the debounce to a new input field, created with innerHTML, but it does not execute on a Web Form field. I’m trying to invoke it with the web_form.on event listener.

I think the error has to do with the debounce function being called repeatedly for the Web Form field but only once, and correctly so, for the inline field created with innerHTML.

I trust there must be a really bright JS developer on this forum who can solve this.

frappe.ready(function() {
	
	function debounce(callback, delay) {
		console.log("debounce");
		let timeout;
		return function() {
			clearTimeout( timeout );
			timeout = setTimeout( callback, delay );
		}
	}

	function validation() {
		console.log("validation")
	}

	var wft_h1 = document.getElementsByClassName("web-form-title")[0].getElementsByTagName("h1")[0];
	wft_h1.innerHTML = '<label for="myInput">Type something</label><br><input id="myInput" type="text"></input>'
	const myInput = document.getElementById("myInput");
	myInput.addEventListener(
		"keyup",
		debounce( validation, 2000 )
	);

	frappe.web_form.on('my_data_fieldtype', (field,value) => {
		console.log("web_form.on");
		debounce( validation, 2000 );
	});
});

Debounce function usage: In the frappe.web_form.on event handler, you’re calling debounce(validation, 2000) directly, which doesn’t work as intended. The debounce function should be used to create a debounced version of the function, not called on each event.

const debouncedValidation = debounce(validation, 2000);

    myInput.addEventListener("keyup", debouncedValidation);

    frappe.web_form.on('my_data_fieldtype', (field, value) => {
        console.log("web_form.on");
        debouncedValidation();
    });
1 Like

Thanks so much @atulbhatt-system32

I get the gist of what you’re saying, but I cannot get either of the 2 fields to now apply the debounce. Have you actually coded this on a Web Form to test it?

Sorry @atulbhatt-system32

It’s working absolutely fine. You’re a :star:
Thanks so much.

I think the const was not being refreshed, hence it only worked when I hard reloaded my page.

Herewith the complete coding for those who wish to implement their own debounce function. I include the test field created with the innerHTML just for fun.

frappe.ready(function() {
	function debounce(callback, delay) {
		console.log("debounce");
		let timeout;
		return function() {
			console.log(timeout);
			clearTimeout(timeout);
			timeout = setTimeout(callback, delay);
		};
	};
	function validation() {
		console.log("validation");
	};
	const debouncedValidation = debounce(validation, 2000);
	frappe.web_form.on('my_data_fieldtype', (field, value) => {
		console.log("web_form.on.my_data_fieldtype");
		debouncedValidation();
	});
	var wft_h1 = document.getElementsByClassName("web-form-title")[0].getElementsByTagName("h1")[0];
	wft_h1.innerHTML = '<label for="myInput">Type something</label><br><input id="myInput" type="text"></input>'
	var myInput = document.getElementById("myInput");
	myInput.addEventListener("keyup", debouncedValidation);
});
1 Like

Hello @atulbhatt-system32

I have been trying to pass the (field, value) arguments to the validation function but cannot succeed.

I suspect it has something to do with …args

https://www.freecodecamp.org/news/javascript-debounce-example/

https://medium.com/@uncoverfrontend/understanding-debounce-in-javascript-enhancing-function-performance-53d6f5155237

I sincerely hope you know how to achieve that.

Can you share the complete code?

I’ve had various attempts at providing the arguments to debouncedValidation(), validation() and debounce(). However it gets messy with every attempt without me actually knowing what I’m doing!!! I’m using the exact same code as provided above.

The linked articles uses a different method to your solution incorporating something such as “…args” which I have no clue of what it is, but assume it is to pass arguments?

frappe.ready(function() {
    function debounce(callback, delay) {
        console.log("debounce");
        let timeout;
        return function(...args) {
            console.log(timeout);
            clearTimeout(timeout);
            timeout = setTimeout(() => callback(...args), delay);
        };
    }

    function validation(arg1, arg2) {
        console.log("validation", arg1, arg2);
    }

    const debouncedValidation = debounce(validation, 2000);

    frappe.web_form.on('my_data_fieldtype', (field, value) => {
        console.log("web_form.on.my_data_fieldtype");
        debouncedValidation(field, value);
    });

    var wft_h1 = document.getElementsByClassName("web-form-title")[0].getElementsByTagName("h1")[0];
    wft_h1.innerHTML = '<label for="myInput">Type something</label><br><input id="myInput" type="text"></input>';
    var myInput = document.getElementById("myInput");
    myInput.addEventListener("keyup", (event) => debouncedValidation(event.target.value, 'keyup'));
});

The ...args is a feature that allows a function to accept an indefinite number of arguments as an array. This is to make the debounce function generic and not just focused on your validation function.