【问题标题】:Disable Button for one minute when it's clicked单击时禁用按钮一分钟
【发布时间】:2021-01-26 07:31:55
【问题描述】:

我正在尝试制作一个按钮,当您单击它时该按钮将被禁用 1 分钟。 仅当按钮启用并单击它时,才应将 25 个点添加到 div(div 从 0 开始)。 每次点击后,按钮将被禁用,计时器将开始运行。

这里有一些图片让整个事情更容易理解:

【问题讨论】:

  • 您能提供到目前为止您尝试过的代码吗?您面临的具体问题是什么?请注意,SO 及其成员不是编码服务。
  • 您可以查看旧解决方案HERE

标签: javascript html timer settimeout disable


【解决方案1】:

可以创建Custom Element
这需要了解以下主题:

基本结构

定义一个自定义元素需要一个扩展HTMLElement(或其子类之一)的JS

然后通过使用其 HTML 元素名称调用 customElements.define() 将此类注册为 HTML 元素(必须在其名称中包含 -(连字符)中,例如“example-element” ) 及其关联的类。

我决定将类称为CounterElement,并将HTML元素称为<counter-instance>

class CounterElement extends HTMLElement {
  constructor() {
    super(); // Required since we are extending a class
  }
}
customElements.define('counter-instance', CounterElement);

设置影子根

Shadow Root 的设置如下:

  1. 通过调用Element.attachShadow() 创建影子根
  2. 创建并附加其 HTML 元素
  3. 为影子根添加样式

由于一些评论提到访问元素仍然很容易,我决定使用{mode: 'closed'} 来阻止不知情的用户访问它们。但是,它并非无法访问。这将需要一个MutationObserver,但这不包含在这个答案中以保持它(有点)简短,heh

使用{mode: 'closed'} 时,需要手动保留对影子根的引用。我决定使用私有成员变量#shadow

现在,需要创建和附加影子根的元素。同样,我为“重要”元素保留了一个私人参考:#samp#button

我还添加了一些样式。

class CounterElement extends HTMLElement {
  #shadow;
  #samp;
  #button;
  
  constructor() {
    super();
    
    // Shadow Root
    this.#shadow = this.attachShadow({mode: 'closed'});
    
    let div = document.createElement('div');
    let style = document.createElement('style');
    style.innerHTML = 'div{padding:0.2rem;border:1px solid black}'
        + 'samp{display:block}button{font-family:monospace}';
    
    this.#samp = document.createElement('samp');
    this.#button = document.createElement('button');
    
    div.append(this.#samp, this.#button);
    this.#shadow.append(style, div);
  }
}
customElements.define('counter-instance', CounterElement);

设置元素

现在所有重要元素都已存在,我们需要正确设置它们。我的意思是添加他们应该有的文本。

由于我们将跟踪计数器和秒,因此将它们声明为成员变量是有意义的。

为了将秒数格式化为mm:ss 格式,我编写了一个实用函数formatTime()。这看起来很方便,所以它可以在全局范围内。
注意,您应该尽量不要让太多变量使全局范围混乱。您可以使用IIFEs 将它们保留在本地。

现在,添加文本就像使用 HTMLElement.innerText 一样简单。

在评论中,您要求在秒数达到 0 时让按钮说“收集”。这可以使用 if-else 语句来完成,但为简洁起见,我使用了 Ternary Operator

function formatTime(seconds) {
  var min = new String(Math.floor(seconds / 60));
  var sec = new String(seconds % 60);

  while (min.length < 2) min = '0' + min;
  while (sec.length < 2) sec = '0' + sec;

  return min + ':' + sec;
};

class CounterElement extends HTMLElement {
  counter = 0;
  seconds = 0;
  #shadow;
  #samp;
  #button;
  
  constructor() {
    super();
    
    // Shadow Root
    // ...
    
    this.updateText();
  }
  
  updateText() {
    this.#samp.innerText = this.counter;
    this.#button.innerText = this.seconds ? formatTime(this.seconds) : 'Collect';
  }
}
customElements.define('counter-instance', CounterElement);

添加(计时器)功能

现在的最后一步是添加计时器和onclick-listener。

由于我们想在点击#button时启动计时器,并在seconds到达0后停止,我们可以分别使用setInterval()clearInterval()

前一个函数返回间隔的 ID,后者使用它的 ID 清除它。要按预期使用这些函数,我们需要保留对区间 ID 的引用。

#button被点击时,它应该:

  • 重置seconds并增加counter
  • 启动我们的计时器
  • 在我们的计时器期间被禁用
  • 根据现在重置的值更新文本

这是我们实现它的(缩短的)代码:

// function formatTime(seconds) () { ... }

class CounterElement extends HTMLElement {
  counter = 0;
  seconds = 0;
  #shadow;
  #samp;
  #button;

  constructor() {
    super();

    // Shadow Root
    // ...
    
    let intervalId;
    let intervalStep = () => { // Will be called every second
      this.updateText();
      if (--this.seconds <= 0) {
        clearInterval(this.#intervalId);
        this.#button.disabled = false;
      }
    };
    
    this.#button.addEventListener('click', () => {
      this.seconds = 60;
      this.counter += 25;
      this.#intervalId = setInterval(intervalStep, 1000);
      this.#button.disabled = true;
      this.updateText();
    });
    
    this.updateText();
  }
  
  // Member-functions ...
}
customElements.define('counter-instance', CounterElement);

结论

如前所述,仍然可以使用开发工具访问影子根的元素。可以使用 MutationObserver 完全禁用更改按钮的 disabled-property 的功能,从而防止属性被更改,具体取决于 seconds 当前拥有的数字。

这里是 Stack-Snippet 的完整代码,您可以自己尝试一下:

function formatTime(seconds) {
  var min = new String(Math.floor(seconds / 60));
  var sec = new String(seconds % 60);

  while (min.length < 2) min = '0' + min;
  while (sec.length < 2) sec = '0' + sec;

  return min + ':' + sec;
};

class CounterElement extends HTMLElement {
  counter = 0;
  seconds = 0;
  #shadow;
  #samp;
  #button;
  
  constructor() {
    super();
    
    // Shadow Root
    this.#shadow = this.attachShadow({mode: 'closed'});
    
    let div = document.createElement('div');
    let style = document.createElement('style');
    style.innerHTML = 'div{padding:0.2rem;border:1px solid black}'
        + 'samp{display:block}button{font-family:monospace}';
    
    this.#samp = document.createElement('samp');
    this.#button = document.createElement('button');
    
    div.append(this.#samp, this.#button);
    this.#shadow.append(style, div);
    
    let intervalId;
    let intervalStep = () => { // Will be called every second
      if (--this.seconds <= 0) {
        clearInterval(intervalId);
        this.#button.disabled = false;
      }
      this.updateText();
    };
    
    this.#button.addEventListener('click', () => {
      this.seconds = 60;
      this.counter += 25;
      intervalId = setInterval(intervalStep, 1000);
      this.#button.disabled = true;
      this.updateText();
    });

    this.updateText();
  }
  
  updateText() {
    this.#samp.innerText = this.counter;
    this.#button.innerText = this.seconds ? formatTime(this.seconds) : 'Collect';
  }
}
customElements.define('counter-instance', CounterElement);
/* Ignore; only for styling purposes */
body {
  display: flex;
  gap: 0.6rem;
}
<!-- Works with multiple elements -->
<counter-instance></counter-instance>
<counter-instance></counter-instance>

【讨论】:

  • 嗨奥斯卡,很抱歉我没有看到你的答案。谢谢你的努力。这就是我需要的:)
【解决方案2】:

这是你的代码

$('#btn').prop('disabled',true);
startCountDown();

function getCounter(){
  return  parseInt($('#counter').html());
}
function setCounter(count) {
  $('#counter').html(count);
}

$("#btn").click(function() {
  setCounter(getCounter()+25);
  $('#btn').prop('disabled',true);
  startCountDown();
});

function startCountDown() {
  var minutes = 0,
    seconds = 59;
  $("#countdown").html(minutes + ":" + seconds);
  var count = setInterval(function() {
    if (parseInt(minutes) < 0 || parseInt(seconds) <=0 ) {
      $("#countdown").html(minutes + ":" + seconds);
      clearInterval(count);
      $('#btn').prop('disabled',false);
    } else {
      $("#countdown").html(minutes + ":" + seconds);
      seconds--;
      if (seconds < 10) seconds = "0" + seconds;
    }
  }, 1000);
}
#counter{
  font-size: 25px;
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="counter">0</div>
<button id="btn">
<span id="countdown">0:00</span>
</button>

【讨论】:

  • 这正是我需要的!但是你知道如何让它不说 0:00 而是“收集”吗?抱歉,我是 JavaScript 新手
  • Here is Code Pen link 您只需要在时钟停止时更改代码我们会将倒计时按钮的内容覆盖为“收集”此外,HTML 中有一个更改,我们必须保持按钮禁用开始所以在倒计时按钮中添加禁用属性
【解决方案3】:

类似:

const button = document.getElementById("myBtn")

function freeze () {
  button.disabled = true
  setTimeout(function() { 
    button.disabled = false
  }, 60000);
}

button.onclick = function () {
 if(button.disabled) { // Avoid user removing disable in the html
  // Add 25 to whatever
    freeze()
  }
}

【讨论】:

    【解决方案4】:

    另一种可能性如下:

    这里是你的按钮:

    <button id="MyBtn" onclick="disable_button_3_secs(); return false;">Disabled me for 3 seconds</button>
    

    这里是你的脚本:

        var timeout_lenght = 3000;
        var myBtn = document.getElementById("MyBtn");
    
        function disable_button_3_secs() {
            myBtn.disabled = true;
            Enable_at_timer_out();
        }
    
        function Enable_at_timer_out() {
            setTimeout(function () {myBtn.disabled = false; }, timeout_lenght);
        }
    

    我个人喜欢更好地控制我的代码,所以我总是尽可能使用变量。我已将计时器设置为 3 秒,但您可以随意更改。

    大卫

    【讨论】:

    • 嗨@Davice。您的解决方案解决了一些问题。即使您在添加disabled: true 时更改了ui,具有html 知识的用户也可以打开DOM 并手动将其删除,然后单击将再次起作用。您还应该在 disable_button_3_secs 方法中控制它。您的回复也不包含 +25 逻辑。我认为您的回复需要更多细节才能更有用。
    • 嗨@rbalet,感谢您指出这一点。我只是想保持简单,因为上一个答案看起来与我的非常相似(除了您建议的改进之外)。
    猜你喜欢
    • 1970-01-01
    • 2023-03-28
    • 2022-12-20
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多