首页 > 解决方案 > 有没有办法用 jQuery 切换提交按钮的行为?(例如显示/隐藏/提交)

问题描述

我正在尝试切换按钮以在单击时提交或显示输入字段,具体取决于用户是否单击文档以隐藏/折叠输入字段。问题是在用户关闭输入字段后,提交按钮不会返回到切换行为;单击按钮再次打开输入字段会导致表单提交。

我需要该按钮在单击时触发搜索字段滑入。一旦输入字段可见,该按钮将提交而不是切换,除非用户单击文档以隐藏该字段。目前,初始单击将打开输入字段,单击文档将折叠该字段。在前一个操作之后的第二次单击将导致“提交”。

我相信,根据我的一点理解,第二次单击时不会发生preventDefault()函数,或者需要以某种方式切换.off('click') ?

HTML:

  <form action="" id="sSearch" class="collapsed">
    <input type="search" id="sSearch-Field" placeholder="Search">
    <button type="submit">
      Open
    </button>
   </form>

JS:

$('#sSearch button').click(function (event) {
  event.preventDefault();
  $('#sSearch').removeClass('collapsed');
  $('#sSearch button').off('click');
});

$(document).click(function (event) {
  $target = $(event.target);
  if(!$target.closest('#sSearch').length && $('#sSearch').not('.collapsed')) {
    $('#sSearch').addClass('collapsed');
  }
});

我不明白为什么preventDefault()函数在第二次点击时不起作用?根据我在类似问题中看到的内容,off()unbind()函数应该重置点击和默认行为。这是真的?

CodePen 示例

标签: javascripthtmljquery

解决方案


这里已经有一个答案,但它没有解释为什么你的代码不起作用。从您的问题中可以清楚地看出您是在专门尝试理解问题,所以让我们解决它!:-)

您询问:

... off() 和 unbind() 函数应该重置点击和默认行为。这是真的?

在这种情况下,是的,这几乎就是发生的事情。更一般地说,off()将删除一个事件处理程序。在这种情况下,它会删除click添加到button.

那么问题出在哪里?该按钮处理程序第一次运行时,它将自己从处理任何未来的点击中移除。这意味着您将永远无法再次显示搜索输入(这是处理程序通常所做的)。如果单击文档隐藏搜索输入,然后需要再次显示,则不能。执行此操作的处理程序不再监听您的按钮的点击!

让我们追踪这是如何发生的:

  • 如果第二次点击是在文档上:

    • 第一个处理程序不附加任何事件,不会触发;
    • 第二个处理程序确实开火;
    • !$target.closest('#sSearch').length将评估true
    • $('#sSearch').not('.collapsed')将评估true
    • 测试通过,搜索字段被隐藏;
    • 好的!这就是你想要的;
  • 现在说你想再次显示搜索输入,所以你点击按钮:

    • 第一个处理程序不附加任何事件,不会触发;
    • 第二个处理程序确实开火;
    • !$target.closest('#sSearch').length将评估false
    • 测试失败,第二个处理程序什么也不做;
    • 两个处理程序都没有做任何事情,没有阻止默认操作 - 表单将提交,就像没有任何 Javascript 的普通 HTML 表单一样;
    • 坏的!你这不是你想要的。

旁注:$('#sSearch').not('.collapsed')有效,但为了可读性和一致性,它可能应该是$('#sSearch').not('.collapsed').length,就像第一个条件一样。

那么,如何获得你想要的行为呢?

要坚持您使用的方法,您需要在隐藏搜索字段时重新附加第一个事件处理程序,以便页面回到初始状态,就像在页面加载时一样。

为此,我会将代码提取到一个函数中,以便您可以从两个地方调用它,以避免重复它:

// New function we can call whenever we need it
function showSearch (event) {
    event.preventDefault();
    $('#sSearch').removeClass('collapsed');
    $('#sSearch button').off('click');
}

// Call our function when button clicked
$('#sSearch button').click(showSearch);

$(document).click(function (event) {
    $target = $(event.target);
    if(!$target.closest('#sSearch').length && $('#sSearch').not('.collapsed')) {
        $('#sSearch').addClass('collapsed');

        // Re-attach the function to button clicks, so page goes back to
        // the initial state, before any button clicks
        $('#sSearch button').click(showSearch);
    }
});

这是一个有效的 JSFiddle(对不起,我从 CodePen 切换过来,我更习惯了)。

或者,虽然您采用的方法效果很好,但我可能会选择更简单的方法。我不会根据用户活动添加或删除事件处理程序,而是跟踪表单的当前状态并使用它来计算发生点击时要做什么。

我们需要某种变量来跟踪状态……但如果你退后一步,你已经有了这样的变量!该类collapsed告诉我们页面的当前状态。您可以使用jQuery 的.hasClass()方法来检查该类是否存在,这将告诉我们页面当前所处的状态:

  • 对于按钮的点击,如果输入当前有collapsed类,你想显示它,所以你阻止提交并删除类。如果输入没有那个类,你什么也不做,并允许提交发生。

    $('#sSearch button').on('click', function(event) {
        if ($('#sSearch').hasClass('collapsed')) {
            event.preventDefault();
            $('#sSearch').removeClass('collapsed');
        }
    });
    
  • 对于不在按钮上的点击,当输入显示时,你想隐藏它;当输入被隐藏时,你什么都不做。

    $(document).on('click', function (event) {
        $target = $(event.target);
        if(!$target.closest('#sSearch').length && !$('#sSearch').hasClass('collapsed')) {
            $('#sSearch').addClass('collapsed');
        }
    });
    

这是另一个 JSFiddle,使用这种不同的方法


推荐阅读