【问题标题】:How to set focus on textarea upon opening a component modal如何在打开组件模式时将焦点设置在 textarea 上
【发布时间】:2020-07-21 11:59:22
【问题描述】:

我正在尝试创建像 Facebook 这样的帖子创建者,当您按下文本区域时,它会弹出另一个带有另一个 Textarea 的表单当我点击旧的 textarea 时弹出的模态这里是一个更好解释的图像

  <textarea class="form-control" placeholder="Write a post..." rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }" @click="postModal();setTextFocus()" @click.right="postModal();setTextFocus()"></textarea>

  <div class="modal-body"><!-- component modal -->
  <textarea class="form-control" v-bind:placeholder="'What is on your a mind, '+name+'?'" rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }" id="focusText" ref="textFocus"></textarea>
    <has-error  :form="post" field="Content"></has-error>
  </div>
methods:{
        postModal(){
            $('#postModal').modal('show');
            $("#myModal").on('shown.bs.modal', function(){
                $('#postModal').find('#focusText').focus();
            });

        },
        setTextFocus()
        {
            this.$refs.textFocus.focus();
        },

编辑我将添加我的页面的完整代码

<script>
    export default {
        //if you want to use the data in props in your axios request or add url parameters add them like this Axios.post('api/like',{id:this.post.id})
        props:['id','name'],
        data(){
            return{
                posts:{},
                userPhoto:'',
                page:1,
                post : new Form({
                    id:'',
                    Content:'',
                    user_id: this.id,
                })
            }
        },
        methods:{
            postModal() {
                $('#postModal').modal('show');
                $("#myModal").on('shown.bs.modal', function() {
                    $('#postModal').find('#focusText').focus();
                });

            },
            setTextFocus() {
                if ('textFocus' in this.$refs) {
                    this.$refs.textFocus.focus();
                }
            },
            //the problem was we used this.loadPosts in created so when the page load it requested data twice one from created and the other
            // one from infinite loading component SO you have to remove loadPosts methods from created() on rely on <infinity-loading> only
            loadPosts(page = this.page){
                Vue.axios.get('api/post?page='+page).then(({data})=>{
                    this.posts=data;
                    this.page=data.current_page;
                });
            },
            addPost(){
                this.$Progress.start();
                this.post.post('api/post').then(()=>{
                    $('#postModal').modal('hide');
                    this.post.reset();
                    Fire.$emit('loadPage');
                    Toast.fire({
                        icon: 'success',
                        title: 'Created successfully'
                    });
                    this.$Progress.finish();
                }).catch(()=>{
                    this.$Progress.fail();

                })
            },
            addLike(post_id){
                Vue.axios.post('api/like',{
                    post_id:post_id,
                    user_id:this.post.user_id,
                }).then(()=>{
                    Fire.$emit('loadPage');
                })
            },
            likedBythisUser(post){
                return post.likes.find(like=>{
                    return like.user_id===this.id && like.post_id===post.id;
                }) // return a boolean value
            },
            countComments(comments){
                const numberOfComments =comments.length;
                return numberOfComments;
            }

        },
        name: "Post",
        mounted() {
            this.setTextFocus()
        },

        created() {
            this.setTextFocus();
            Vue.axios.post('/user_photo').then((data)=>{
                this.userPhoto=data.data;
            });
            this.loadPosts();
            Fire.$on('loadPage',()=>{
                this.loadPosts();
            });

        },

    }
</script>
<style scoped>
    .panel-shadow {
        box-shadow: rgba(0, 0, 0, 0.3) 7px 7px 7px;
    }
    .panel-white {
        border: 1px solid #dddddd;
    }
    .panel-white  .panel-heading {
        color: #333;
        background-color: #fff;
        border-color: #ddd;
    }
    .panel-white  .panel-footer {
        background-color: #fff;
        border-color: #ddd;
    }

    .post .post-heading {
        height: 95px;
        padding: 20px 15px;
    }
    .post .post-heading .avatar {
        width: 60px;
        height: 60px;
        display: block;
        margin-right: 15px;
    }
    .post .post-heading .meta .title {
        margin-bottom: 0;
    }
    .post .post-heading .meta .title a {
        color: black;
    }
    .post .post-heading .meta .title a:hover {
        color: #aaaaaa;
    }
    .post .post-heading .meta .time {
        margin-top: 8px;
        color: #999;
    }
    .post .post-image .image {
        width: 100%;
        height: auto;
    }
    .post .post-description {
        padding: 15px;
    }
    .post .post-description p {
        font-size: 14px;
    }
    .post .post-description .stats {
        margin-top: 20px;
    }
    .post .post-description .stats .stat-item {
        display: inline-block;
        margin-right: 15px;
    }
    .post .post-description .stats .stat-item .icon {
        margin-right: 8px;
    }
    .post .post-footer {
        border-top: 1px solid #ddd;
        padding: 15px;
    }
    .post .post-footer .input-group-addon a {
        color: #454545;
    }
    .post .post-footer .comments-list {
        padding: 0;
        margin-top: 20px;
        list-style-type: none;
    }
    .post .post-footer .comments-list .comment {
        display: block;
        width: 100%;
        margin: 20px 0;
    }
    .post .post-footer .comments-list .comment .avatar {
        width: 35px;
        height: 35px;
    }
    .post .post-footer .comments-list .comment .comment-heading {
        display: block;
        width: 100%;
    }
    .post .post-footer .comments-list .comment .comment-heading .user {
        font-size: 14px;
        font-weight: bold;
        display: inline;
        margin-top: 0;
        margin-right: 10px;
    }
    .post .post-footer .comments-list .comment .comment-heading .time {
        font-size: 12px;
        color: #aaa;
        margin-top: 0;
        display: inline;
    }
    .post .post-footer .comments-list .comment .comment-body {
        margin-left: 50px;
    }
    .post .post-footer .comments-list .comment > .comments-list {
        margin-left: 50px;
    }
</style>
<template>
    <div class="form-group">
        <div class="col-sm-8"  >
            <div class="comment-wrapper">
                <div class="panel panel-info">
                    <form>
                        <div class="panel-body">
                            <textarea class="form-control" placeholder="Write a post..." rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }" @click="setTextFocus(); postModal();"></textarea> <!-- here is the first textbox -->
                            <has-error  :form="post" field="Content"></has-error>
                            <br>
                            <button type="submit" class="btn btn-info pull-right" @click.prevent="addPost()">Post</button>
                            <div class="clearfix"></div>
                            <hr>
                        </div>
                    </form>
                </div>
            </div>
            <div class="panel panel-white post panel-shadow" v-for="post in posts.data" >
                <div class="post-heading">
                    <div class="pull-left image">
                        <img v-bind:src="'img/profile/' + post.user.photo" class="img-circle avatar" alt="user profile image">
                    </div>

                    <div class="pull-left meta">
                        <i class="dropdown" style="margin-left:550px;position: relative;">
                            <button class="dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" style="background-color:#f8fafc; border: none">
                                ...
                            </button>
                            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
                                <a class="dropdown-item" href="#">Edit post</a>
                                <a class="dropdown-item" href="#">Delete</a>
                            </div>
                        </i>
                        <div class="title h5">
                            <a href="#"><b>{{post.user.name}}  </b></a>
                            made a post.
                        </div>

                        <!-- | hour is called filter and we can stack many filters at the same time-->
                        <h6 class="text-muted time">{{post.created_at | hour}}</h6>
                    </div>
                </div>
                <div class="post-description">
                    <p>{{post.content}}</p>
                    <div class="stats">
                        <button class="btn btn-default stat-item"  @click.prevent="addLike(post.id)">
                            <i class="fa fa-thumbs-o-up" aria-hidden="false" style="color: blue" v-bind:style="likedBythisUser(post)?'color: blue;':'color: gray;'"  > Like &nbsp;{{post.likes.length}}
                            </i> 
                        </button>
                        <a class="btn btn-default stat-item" @click.prevent>
                            <i class="fa fa-reply-all" v-html="countComments(post.comments)"></i> Comments
                        </a>
                    </div>
                </div>
                <comment-input :post="post" :userId="id" :userPhoto="userPhoto"></comment-input>
                <ul class="comments-list" v-for="comment in post.comments?post.comments:''">
                    <comments :comment="comment" :userId="id" :userPhoto="userPhoto"></comments>
                </ul>
            </div>
            <pagination :data="posts" @pagination-change-page="loadPosts"></pagination>
            <hr>
            <!-- Post Modal -->
            <div class="modal fade" id="postModal"  role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style="margin-right: 100px">
                <div class="modal-dialog" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="exampleModalLabel">Create Post</h5>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <textarea class="form-control" v-bind:placeholder="'What is on your a mind, '+name+'?'" rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }" id="focusText" ref="textFocus"></textarea> <!-- here is my second textarea i want it to appear inside my bootstrap modal -->
                            <has-error  :form="post" field="Content"></has-error>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                            <button type="submit" class="btn btn-info pull-right" @click.prevent="addPost()">Post</button>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
</template>

【问题讨论】:

  • alligator.io/vuejs/component-lifecycle 我假设您需要一个 mounted 钩子的实现来设置焦点,以便在创建组件视图后执行。
  • 没有用我用完整的代码页编辑了帖子我希望它可以帮助

标签: javascript jquery vue.js vuejs2 vue-component


【解决方案1】:

您的策略不起作用的原因是,很有可能,this.$refs.textFocus 尚不存在于 DOM 中,因此集中它不起作用。

您可以专注于mounted lifecycle hook 中的文本区域,并在解决$vm.nextTick() 承诺之后,这样您就可以确定该元素确实存在于 DOM 中:

mounted() {
  this.$nextTick(() => this.$refs.textFocus.focus());
}

【讨论】:

    【解决方案2】:

    在挂载的钩子中添加函数 setTextFocus。

    mounted() {
      this.setTextFocus()
    },
    
    methods: {
      postModal() {
        $('#postModal').modal('show');
        $("#myModal").on('shown.bs.modal', function() {
          $('#postModal').find('#focusText').focus();
        });
    
      },
      setTextFocus() {
        if ('textFocus' in this.$refs) {
            this.$refs.textFocus.focus();
        }
      },
    }
    <textarea
      class="form-control"
      placeholder="Write a post..."
      rows="3"
      v-model="post.Content"
      :class="{ 'is-invalid': post.errors.has('Content') }"
      @click="postModal();setTextFocus()"
      @click.right="postModal();setTextFocus()">
     </textarea>
    
    <div class="modal-body">
      <!-- component modal -->
      <textarea
        class="form-control"
        v-bind:placeholder="'What is on your a mind, '+name+'?'"
        rows="3"
        v-model="post.Content"
        :class="{ 'is-invalid': post.errors.has('Content') }"
        id="focusText"
        ref="textFocus">
       </textarea>
      <has-error :form="post" field="Content"></has-error>
    </div>

    【讨论】:

    • 我试过了,但没用modal .. 我想提一下,我没有将模态包含在单独的组件中
    【解决方案3】:

    尝试使用data 值显示模式

    将modal的状态默认设置为false

        data: () => {
            return {
                show: false
            }
        },
    

    然后,使用v-show 显示/隐藏模式(我删除了一些属性)

    <div>
        <textarea class="form-control" placeholder="Write a post..." rows="3" @click="postModal()">
        </textarea>
    
        <div class="modal-body" v-show="show"><!-- component modal -->
            <textarea class="form-control" rows="3"
                      id="focusText"
                      ref="textFocus"></textarea>
        </div>
    </div>
    

    现在,在postModal 方法中,将show 的值设置为true

    postModal: function () {
        this.show = true;
        setTimeout(() => {
            this.$refs.textFocus.focus(); // calling the focus after a timeout
        }, 0);
    }
    

    这对我有用。

    注意:

    • 需要超时,因为 focus 应该在单击另一个 textarea 时执行。
    • 超时中的0 表示,如果有的话,在相关进程之后运行代码。
    • 我删除了一些属性来创建一个简单的测试代码:)。
    • 最好让JQuery 远离Vue

    【讨论】:

    • 我无法使用 v-show 切换引导模式 感谢您的帮助,但对我没有用。
    • 好的,知道了。在$('#postModal').modal('show'); 之后,您可以在我的回答中尝试超时吗?
    • 是的,我也试过了,但没用,反正我找到了我的问题的解决方案,你可以查看我的答案,谢谢你的支持:)
    【解决方案4】:

    好吧,在搜索了几个小时后,我找到了解决这个问题的方法,它实际上非常简单,我只需要查看 Modal 引导文档,所以这里是节省您时间的解决方案

     methods:{
                postModal() {
                    $('#postModal').modal('show');
                    $('#postModal').on('shown.bs.modal', function () {
                        $('#textFocus').trigger('focus')
                    });
                },
                }
    <textarea class="form-control" placeholder="Write a post..." rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }" @click="postModal()" ></textarea>
    
    <textarea class="form-control" v-bind:placeholder="'What is on your a mind, '+name+'?'" rows="3" v-model="post.Content"  :class="{ 'is-invalid': post.errors.has('Content') }"  id="textFocus" ref="textFocus"></textarea>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多