Fit.UI icon indicating copy to clipboard operation
Fit.UI copied to clipboard

Make OnFocus and OnBlur fire synchronously

Open FlowIT-JIT opened this issue 3 years ago • 2 comments

The OnFocus and OnBlur events fire asynchronously which might cause problems.

Imagine a control having focus, and we push changes to state when OnBlur fires. If we have a Save button which immediately persist state, then it won't persist the latest changes from our control, since OnBlur won't be able to push changes in time to be included in the payload.

We might be able to solve this with an implementation similar to this (IE not supported!):

https://jsfiddle.net/we5c6sqv/9/

<input>
<div tabindex="0" id="control">
  <span tabindex="0">
    Hello world
    <span tabindex="0">Hello world - again</span>
  </span><br>
  <input tabindex="0">
</div>
<input>
var c = document.querySelector("#control");

c.addEventListener("focusin", function(e)
{
    //console.log("Focus", e.target, e.relatedTarget, document.activeElement);
	
	var fromOutside = e.relatedTarget === null || (e.relatedTarget !== c && Fit.Dom.Contained(c, e.relatedTarget) === false);
	var receiving = e.target === c || Fit.Dom.Contained(c, e.target) === true;

    if (fromOutside && receiving)
	{
        console.log("%c --- Firing OnFocus", "color: green");
		console.log(" --- Element focused at this point: ", document.activeElement);
    }
});

c.addEventListener("focusout", function(e)
{
    //console.log("Blur", e.target, e.relatedTarget, document.activeElement);
	
	var receiving = e.relatedTarget !== null && (e.relatedTarget === c || Fit.Dom.Contained(c, e.relatedTarget) === true);

    if (receiving === false)
	{
        console.log("%c --- Firing OnBlur", "color: blue");
		console.log(" --- Element focused at this point: ", document.activeElement);
    }
});

/*c.addEventListener("blur", function(e)
{
    console.log("OnBlur - Element focused at this point: ", document.activeElement);
});*/
div, span, input
{
  display: inline-block;
  border: 1px solid red;
  margin: 0.5em;
}

div
{
  border: 2px solid green;
}

*:focus
{
  outline: 2px solid blue;
}

FlowIT-JIT avatar Mar 03 '23 14:03 FlowIT-JIT

Notice that when onfocusout (or onblur for that matter) fires, focus has not yet been assigned to another element, so document.activeElement will temporarily return <body>, as the example demonstrates. So if we want to be able to obtain the element soon to be focused, we might need to provide it to the event handlers like demonstrated below. control.OnBlur(function(sender: Fit.Controls.ControlBase, args: { Target: HTMLElement, Origin: HTMLElement | null }) {}); Basically we just need to forward e.target and e.relatedTarget.

FlowIT-JIT avatar Mar 03 '23 14:03 FlowIT-JIT

Example improved to prove it respects expected event order when control lose focus: OnMouseDown => Control's OnBlur => OnMouseUp => OnClick https://jsfiddle.net/6qzenLcg/

FlowIT-JIT avatar Mar 10 '23 10:03 FlowIT-JIT