folks!
Does anyone know the opposite method to cellFocused in ag-grid?
I need to detect when the focused cell loses its focus and run some actions.
Thanks for your responses.
I've found a way to support onBlur event. Since ag-grid doesn't have a built-in method, I created wy own event listener to the focus cell node and remove it after losing the focus state.
So, my code looks like this. Inside the react class I have 3 additional methods:
removeCellBlurListener = () => {
const target = document.activeElement;
if (target) {
target.removeEventListener('blur', this.onCellBlur);
}
};
addCellBlurListener = () => {
const target = document.activeElement;
if (target) {
target.addEventListener('blur', this.onCellBlur);
}
};
onCellBlur = () => {
...do something on blur
};
render () {
return (
<AgGridReact
{...restProps}
onCellFocused={(e) => this.addCellBlurListener()}
onGridReady={this.onGridReady}
/>
);
}
Related
Suppose I've got the Code structure below. If I focus an element within my red container (one of the inputs), the focusin event triggers. Likewise, if I click outside the red container, the focusout event triggers.
However, if I click one of the input elements, then directly the other, both a focusout and a focusin event get triggered in quick succession.
Is there an easy way to avoid this or find out whether the second focusout event can be ignored because focus in fact stays within the relevant element, aside from ugly solutions like setting a flag on the first focusout event and waiting for a render tick to see whether another focusin event happens?
document.getElementById("el").addEventListener("focusin",
() => document.getElementById("out").innerHTML += "focusin<br/>");
document.getElementById("el").addEventListener("focusout",
() => document.getElementById("out").innerHTML += "focusout<br/>");
<div id="el" style="background-color: red; padding: 4px">
<input />
<input />
</div>
<div id="out">
</div>
As there doesn't seem to be an easy solution, I've wrote the ugly solution of waiting for the next render tick. I wrote it as reusable React hook, so if it helps someone, here it is.
export const useFocusWithin = (
element: HTMLElement | undefined,
onFocusIn?: () => void,
onFocusOut?: () => void,
) => {
const [focusWithin, setFocusWithin] = useState(false);
const isLoosingFocusFlag = useRef(false);
useHtmlElementEventListener(element, 'focusin', () => {
setFocusWithin(true);
onFocusIn?.();
if (isLoosingFocusFlag.current) {
isLoosingFocusFlag.current = false;
}
});
useHtmlElementEventListener(element, 'focusout', (e) => {
isLoosingFocusFlag.current = true;
setTimeout(() => {
if (isLoosingFocusFlag.current) {
onFocusOut?.();
isLoosingFocusFlag.current = false;
setFocusWithin(false);
}
});
});
return focusWithin;
};
export const useHtmlElementEventListener = <K extends keyof HTMLElementEventMap>(
element: HTMLElement | undefined, type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any) => {
useEffect(() => {
if (element) {
element.addEventListener(type, listener as any);
return () => element.removeEventListener(type, listener as any);
}
}, [element, listener, type]);
};
My intention is to update the 'isEditorFocused' state whenever the focused element changed, and if the div contains the focused element, deliver true into the Editor component.
However, the code does not work as my intention... It updates state only the first two times.
This is my Code. Actually not the exact code, but it is the core part of my question. If there is any typo, please ignore it. I checked it all in my real code file.
export default AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
const editorRef = React.useRef(null);
React.useEffect(() => {
if(editorRef.current !== null) {
if(editorRef.current.contains(document.activeElement)
setIsEditorFocused(true);
else
setIsEditorFocused(false);
}
}, [document.activeElement]}
return (
<div ref={editorRef} tabIndex="0">
<Editor isEditorFocused={isEditorFocused}></Editor>
<FileUploader {some props}/>
</div>
)
}
Not to completely change your code, but couldn't you just use onFocus and onBlur handlers?
For example:
const AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
return (
<div
onFocus={() => {
setIsEditorFocused(true);
}}
onBlur={() => {
setIsEditorFocused(false);
}}
tabIndex="0"
>
<Editor isEditorFocused={isEditorFocused}></Editor>
</div>
);
};
Working codepen
As T J mentions so eloquently, your issue is with document.activeElement
Note regarding React's current support for onFocus vs onFocusIn:
React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React.
Source: React Github
The main problem is this: [document.activeElement].
The useEffect dependency array only works with React state, and document.activeElement is not React state.
You can try using a focusin event listener on your <div>, if it receives the event it means itself or something inside it got focus, since focusin event bubbles as long as nothing inside is explicitly preventing propagation of this event.
try this way.
const AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
const handleBlur = (e) => {
setIsEditorFocused(false)
};
handleFocus = (){
const currentTarget = e.currentTarget;
if (!currentTarget.contains(document.activeElement)) {
setIsEditorFocused(true);
}
}
return (
<div onBlur={handleBlur} onFocus={handleFocus}>
<Editor isEditorFocused={isEditorFocused}></Editor>
</div>
);
};
In my react application, I have blur event listener, in my listener function I want to know, on which the focus went to. How can I get to know that in chrome browser?
The below is my onBlur event function
const handleBlurOfDateInput = (date, event) => {
const valueEntered = datetoMomentDate(date);
// Here i want to check, which element got focused using event
hide && hide();
onChange(valueEntered);
};
You can use event.target, or document.getElementFromPoint(event.pageX, event.pageY)
const handleBlurOfDateInput = (date, event) => {
const valueEntered = datetoMomentDate(date);
// Here i want to check, which element got focused using event
hide && hide();
onChange(valueEntered);
};
const handleBlurOfDateInput = (date, event) => {
const valueEntered = datetoMomentDate(date);
if(event.relatedTarget){console.log(event.relatedTarget)}
hide && hide();
onChange(valueEntered);
};
You can use relatedTarget.
Notice that relatedTarget will return null if the element focused is not an input. In this case, you can use tabIndex = '0' for that element.
Please read code first.
After css processing, it seems like memo application's single memo paper.
The goal of the component is to print a 1 when clicked(in real, the goal is to hadding redux store's state).
When i click outside of div component, it works very well. ( it printed '1' )
but when i clicked inner div component(title, date,content), onClick event also proceed ( it printed '')
how can i prevent non-valued print?
My code :
class container extends Component {
handleState = (event) => {
console.log(event.target.id)
}
render(){
return(
<div onClick={handleState} id={value}>
<div>title</div>
<div>date</div>
<div>content</div>
</div>
)
}
}
container.defaultprops = {
value: 1
}
thanks.
You can use currentTarget:
handleState = (event) => {
console.log(event.currentTarget.id)
}
About difference between target and currentTarget:
https://stackoverflow.com/a/10086501/5709697
You can use currentTarget to check if it's the target since you bound the handler to the parent e.g.
handleState = (event) = > {
if (event.target == event.currentTarget) {
console.log(event.target.id)
}
}
I'm using the React Grid component and I'm looking for a way to fire a function when double click a row.
I found a rowClick function and I can use it now to select a row or handle an onClick event : <Grid rowClick={e => yourFunction(e)}> .
But there is no function to handle a doubleClick event.
This is my approach, I passed a onDoubleClick() function as props to my component, and bind it with the listener of doubleClick on componentDidMount for each row :
componentDidMount() {
let { onDoubleClick } = this.props;
if (onDoubleClick) {
const rows = document
.getElementsByClassName('k-widget k-grid')[0]
.getElementsByClassName('k-grid-table')[0]
.getElementsByTagName('tbody')[0]
.getElementsByTagName('tr');
for (let i = 0; i < rows.length; i++) {
rows[i].addEventListener('dblclick', () => onDoubleClick());
}
}
}
For the moment this works but I'm not able to pass the clicked row data to my function.
Is there any hack to retrieve the row's data using the element ?
Like this for example : onDoubleClick(kendo.data.DataSource(rows[i])) => return the json data to function.
The grid have a rowRender property, that can be used as a RenderProp for fully modifying the rows, including attaching them a rowClick using the native React approach.
rowRender(trElement, dataItem) {
const trProps = {
...trElement.props,
onDoubleClick: () => {
//place your logic here
}
};
return React.cloneElement(trElement, { ...trProps }, trElement.props.children);
}
You may find this live example for how to attach mouse mousedown, blur and focus for the GridRow in the InCell editing demo The same logic for the onDoubleClick can be used as in my code-snipped above.