7

So I've set up a file to setState() for onBlur and onFocus in the SocialPost.js file. But when I onClick a <div> in the SocialPostList.js (the parent) where it activates the parameterClicked() function in the SocialPost.js file, the <input> in SocialPost.js becomes blurred.

How do I make it so that the <button> onClick in SocialPostList.js does not take the focus() from the <input> in SocialPost.js?

I've tried e.preventDefault() and e.stopPropagation() without success. The files are below, any help would be appreciated!!!

SocialPostList.js

import React, { Component } from 'react'
import { graphql, gql } from 'react-apollo'
import SocialPost from './SocialPost'

class SocialPostList extends Component {
    render() {
        const PostListArray = () => {
            return(
            <div onClick={(e) => {e.preventDefault(); e.stopPropagation()}}>
                {this.props.allParametersQuery.allParameters.map((parameter, index) => (
                    <div
                        key={index}
                        onClick={(e) => {e.preventDefault();e.stopPropagation();this.child.parameterClicked(parameter.param, parameter.id)}}
                        >{'{{' + parameter.param + '}}'}</div>
                ))}
            </div>)
        }
        return (
            <div>
                ...
                <PostListArray />
             {this.props.allSocialPostsQuery.allSocialPosts.map((socialPost, index) => (
                    <SocialPost
                        ref={instance => {this.child = instance}}
                        key={socialPost.id}
                        socialPost={socialPost}
                        index={index}
                        deleteSocialPost={this._handleDeleteSocialPost}
                        updateSocialPost={this._handleUpdateSocialPost}
                        allParametersQuery={this.props.allParametersQuery}/>
                ))}
                ...
            </div>
        )
    }

}

const ALL_SOCIAL_POSTS_QUERY = gql`
  query AllSocialPostsQuery {
    allSocialPosts {
          id
          default
          message
        }}`

export default graphql(ALL_SOCIAL_POSTS_QUERY, {name: 'allSocialPostsQuery'})(SocialPostList)

SocialPost.js

import React, { Component } from 'react'
class SocialPost extends Component {
    constructor(props) {
        super(props)
        this.state = {
            message: this.props.socialPost.message,
            focus: false
        }
        this._onBlur = this._onBlur.bind(this)
        this._onFocus = this._onFocus.bind(this)
    }
    _onBlur() {
        setTimeout(() => {
            if (this.state.focus) {
                this.setState({ focus: false });
            }}, 0);
    }
    _onFocus() {
        if (!this.state.focus) {
            this.setState({ focus: true });
        }
    }
    render() {
        return (
            <div className='socialpostbox mb1'>
                <div className='flex'>
                    <input
                        onFocus={this._onFocus}
                        onBlur={this._onBlur}
                        type='text'
                        value={this.state.message}
                        onChange={(e) => { this.setState({ message: e.target.value})}}/>
                </div>
            </div>
        )
    }
    parameterClicked = (parameterParam) =>{
        if (!this.state.focus) return
        let message = this.state.message
        let newMessage = message.concat(' ' + parameterParam)
        this.setState({ message: newMessage })
}

export default SocialPost
Kevin Danikowski
  • 4,620
  • 6
  • 41
  • 75
  • 1
    Returning false in the .click function is another way to do this. Using `e.preventDefault(); return false;` might work – pfg Oct 24 '17 at 19:21
  • @pfg thank you for the comment, I've tried it without luck, I tried it again and no luck still :/ any other suggestions? – Kevin Danikowski Oct 24 '17 at 19:25
  • 1
    I guess you could have .click(() => .focus) the text field if it's only one – pfg Oct 24 '17 at 19:27
  • sadly I have an array of them, but could you elaborate at where I would put that? – Kevin Danikowski Oct 24 '17 at 19:28
  • 1
    If you could do that, you would put it in the button .click function and tell it that when it is selected, select the previously selected input. – pfg Oct 24 '17 at 19:31
  • 1
    You can't stop the blur event from firing, but you can put the focus back when clicking the post, as @pfg mentioned. – Daniel Andrei Oct 24 '17 at 20:03
  • @pfg and @DanielAndrei, I changed the onClick to `let lastFocusedElement = document.activeElement; console.log(lastFocusedElement); lastFocusedElement.focus(); this.child.parameterClicked(parameter.param, parameter.id)` however the document.activeElement returns the body, any idea how to avoid this? I'm looking up answers with no avail – Kevin Danikowski Oct 24 '17 at 20:05
  • 1
    Trying to work out the focused element at that point is too late. You need to plan ahead by getting the input field to save the id when it is focused. Problem is, you will need to do that for every field/object in your form. Maybe you need to rethink based on what you CAN do as opposed to what you want to do – Mikkel Oct 24 '17 at 20:29
  • See my answer on this question for some clarification https://stackoverflow.com/a/70870755/2180570 – V. Rubinetti Jan 26 '22 at 21:55

1 Answers1

2

Well, I don't think that's a React thing. It appears the blur event fires before the onClick, so the latter cannot prevent the former, and I'd expect event.stopPropagation to stop events bubbling from child to parent, not the other way around. In other words, I don't know how to stop it.

In all fairness this behaviour is expected - clicking somewhere else makes you lose focus. That said, here and elsewhere a solution is presented where you set up a flag on mouse down. Then, when blur fires, if it encounters the 'click flag' it may abstain from producing effects and may even refocus back.

If you choose to refocus, it is trivial to save a reference to the button or input, or querySelecting it (it's not too late or anything like that). Just be cautious that it is all too easy to set focus traps or mess up navigation for screen readers when you mix focus and javascript.

isacvale
  • 627
  • 8
  • 13