Jemt / Fit.UI

Fit.UI is a JavaScript based UI framework built on Object Oriented principles
http://fitui.org
GNU Lesser General Public License v3.0
19 stars 7 forks source link

Make OnFocus and OnBlur fire synchronously #181

Open FlowIT-JIT opened 1 year ago

FlowIT-JIT commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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