单击一次按钮






2.38/5 (14投票s)
2006 年 8 月 4 日
5分钟阅读

74303

637
单击按钮/图像按钮时;禁用页面上的所有按钮,这可以确保该按钮只单击一次,而不是不小心单击两次。
引言
这是一个自定义的 Button
/ImgBtn
,它可以禁用页面上的所有其他按钮(Image Button、<button>
、类型为 Submit
、Reset
、Button
的 input 元素),一旦按钮被禁用,它们将无法被单击,直到被控件本身重新启用。此功能可确保按钮只单击一次,而不是连续单击多次,而且由于它禁用了页面上的所有其他按钮,因此在一个按钮的处理过程中,不会出现单击其他按钮的情况。
背后的原理
每当单击此 Custom Button/ImgBtn
时,我都会搜索页面上的所有按钮,创建这些按钮的克隆,禁用它们并将它们置于原始按钮之上,这就产生了按钮被禁用的错觉。我避免修改现有按钮的原因是,我不想中断按钮禁用它们时的处理过程,而且我还想保留 document.activeElement
和 btn.Click()
等功能。
对于 ImageButton
,在创建 ImageButton
的克隆时,我将图像的 URL 更改为禁用的,所以必须在同一图像路径中有一个禁用的图像。
特点
- 您无需编写任何额外的代码,只需将此控件包含在页面中即可,如果您使用的是
ImageButton
。 - 按钮的禁用发生在 JavaScript 中,因此按钮在服务器进行任何长时间处理之前就会被禁用。
- 页面上可以有任何类型的按钮,所有按钮都将被禁用。
- 按钮被禁用是为了一个特定的时间间隔,这个时间间隔可以为不同的自定义按钮增加或减少。
- 在控件重新启用所有按钮之前,如果页面提交到服务器并重新加载,您可以看到所有按钮都已重新启用,因为所有操作都在客户端进行,因此状态不会被保留。
- JavaScript 函数(
EnableCustomButton()
)是公开的,通过这些函数可以重新启用所有按钮,甚至在达到启用间隔之前,例如,如果验证失败,您可以向最终用户发出警报并立即重新启用按钮。 - 存在一个 JavaScript 函数,您可以利用该函数禁用所有按钮,甚至无需使用提供的自定义按钮,当使用您自己的控件或 HTML 按钮但仍希望在单击按钮时禁用页面时,此 JS 方法可能会很有用。
必备组件
- 为了显示禁用的图像,必须在同一图像路径中存在“
XXX_disable.XXX
”,即,如果您的 Image 按钮 URL 是“add.gif
”或“btnXLS.jpg
”,您还必须在同一图像路径中拥有“add_disable.gif
”和“btnXLS_disable.jpg
”。 - 每个按钮输入类型(
submit
、reset
、button
)、BUTTON
标签和Image Button
都必须定义 ID。 - 如果您不使用此
CustomImageButton
,但仍希望其他普通按钮在单击时禁用整个页面,则需要在按钮的“onclick
”事件中调用 JavaScript 函数“CustomButtonClicked(this)
”。
例如,在上例中,“Final Save”按钮是一个普通的 ASPButton
,但单击它时,它也会禁用整个页面,代码如下:
btnFinalSave.Attributes.Add("onclick","CustomButtonClicked(this)");
属性
DisableTimeOut
:单击CustomImageButton
后,页面上的所有按钮将禁用多长时间。默认值:8000
。PutDisableScriptAtEnd
:按钮的禁用发生在“JavaScriptonclick
事件”之后。默认值:false
。示例,当
PutDisableScriptAtEnd
=false
时<input onclick="CustomButtonClicked(this); if ( !SomeClientSideValidation() ) return false;" ../>
当
PutDisableScriptAtEnd
=true
时<input onclick="if ( !SomeClientSideValidation() ) return false; CustomButtonClicked(this);" ../>
Using the Code
使用 CustomImageButton
<Tittle:CustomImageButton id=btnXLS onclick=btnExport_Click Runat="server"
ImageUrl="Images/btnExportToExcel.gif">
其 HTML 代码如下:
<input onclick="CustomButtonClicked(this);" type="image" name="btnXLS" id="btnXLS"
onclick src="Images/btnExportToExcel.gif" alt="" border="0" />
上面的代码将渲染为,单击时,禁用页面上的所有按钮,即使其他任何按钮上都没有编写任何内容,也不一定需要其他按钮是同一种类型 CustomImageButton
。
单击普通按钮时禁用所有按钮
X.aspx
<asp:button id="btnFinalSave" Text="Final Save" Runat="server" onclick="btnFinalSave_Click" />
X.aspx.cs
btnFinalSave.Attributes.Add("onclick","CustomButtonClicked(this)");
上面的代码将渲染为如下所示,单击时,禁用页面上的所有按钮。
即使在实际间隔之前也启用按钮,例如,在验证失败时;立即重新启用按钮
X.aspx
<Tittle:CustomImageButton id="btnSave" ImageUrl="Images/btSave.gif"
Runat="server" onclick="btnSave_Click" />
<script language="javascript">
function SomeClientSideValidation()
{
if ( !confirm('Validation not met.\n\nYes - will still save
the page\nNo - will not save the page and re-Enable all buttons') )
{
EnableCustomButton('btnSave');
return false;
}
return true;
}
</script>
X.aspx.cs
btnSave.Attributes.Add("onclick","if ( !SomeClientSideValidation() ) return false;");
技术实现解析
CustomImageButton.cs
..
public class CustomImageButton : ImageButton
{
public int DisableTimeOut ..
public int PutDisableScriptAtEnd ..
..
protected override void OnPreRender(EventArgs e)
{
#region On Pre Render Javascript
//CustomButtonJavascript
if (!Page.IsClientScriptBlockRegistered("CustomButtonScript") )
{
string CustomButtonScript = @"
<script language="javascript">
// TittleJoseph@yahoo.com
// https://codeproject.org.cn/script/articles/list_articles.asp?userid=1654009
var idOfImageButtonsToDisable = '';
var idOfButtonsToDisable = '';
//Custom Image Button Clicked.
function CustomButtonClicked(obj)
{
idOfImageButtonsToDisable = '';
if ( obj.disabled == false )
{
var pgForm = obj.form;
//note: unfortunately type=image never comes up in
//Form.Elements therefore searching them by tag name INPUT
//search all type=image, button, submit, reset and disabling them.
var inputs = pgForm.getElementsByTagName('input');
for ( var i=inputs.length-1; i>=0; i--)
{
try
{
var currentButton = inputs[i];
if ( (currentButton.type == 'submit' || currentButton.type == 'button'
|| currentButton.type == 'reset') && currentButton.disabled == false )
{
//storing ids of all buttons, will require when enabled back later.
idOfButtonsToDisable += (idOfButtonsToDisable==''?'':',') + currentButton.id
CreateDuplicateButtonOverSame(currentButton);
}
//Image Buttons and not if already disabled
//(duplicate button will always be disabled.)
if ( currentButton.type == 'image' && currentButton.disabled == false )
{
//If image button is already disabled, do not touch it.
if ( currentButton.src.toLowerCase().indexOf('_disable.') >= 0 )
continue;
//storing ids of all image buttons, will require when enabled back later.
idOfImageButtonsToDisable += (idOfImageButtonsToDisable==''?'':',') +
currentButton.id ;
CreateDuplicateButtonOverSame(currentButton);
}
}
catch(e)
{
//alert(e.description);
}
}
//Searching all <BUTTON> tags and disabling them as well.
var buttons = pgForm.getElementsByTagName('button');
for ( var i=buttons.length-1; i>=0; i--)
{
try
{
var currentButton = buttons[i];
//storing ids of all buttons, will require when enabled back later.
idOfButtonsToDisable += (idOfButtonsToDisable==''?'':',') +
currentButton.id
CreateDuplicateButtonOverSame(currentButton);
}
catch(e)
{
//alert(e.description);
}
}
setTimeout('ReEnableAllButtons(\''+obj.id+'\')',"+
this.DisableTimeOut.ToString() +@");
}
}
function CreateDuplicateButtonOverSame(currentButton)
{
//Create a duplicate button same as this 1 and disabling that.
var dummyBtn = currentButton.cloneNode();
dummyBtn.id = dummyBtn.id+'_clone';
dummyBtn.disabled = true;
//ERROR: don't know why, but name attribute is not resetting here,
//still removing 'name' as it is conflicting
var nm = dummyBtn.getAttribute('name');
dummyBtn.removeAttribute('name');
dummyBtn.setAttribute('name',nm+'_clone');
dummyBtn.setAttribute('onclick','');
dummyBtn.style.display = 'none';
//Inserting duplicate buttons before end of body
document.body.insertAdjacentElement('beforeEnd',dummyBtn);
//Getting co-ordinate of original image buttons.
var mouseY = document.body.scrollTop +
parseFloat(currentButton.getBoundingClientRect().top);
var mouseX = document.body.scrollLeft +
parseFloat(currentButton.getBoundingClientRect().left);
//co-ordinates which are received are 2 pixel extra and
//do not fully cover the actual object
mouseX = (mouseX >= 2)?mouseX-2:mouseX;
mouseY = (mouseY >= 2)?mouseY-2:mouseY;
//I'm not hiding actual button because document.activeElement
//will not be available then,
//just placing disable image/button on top of actual button/image button.
currentButton.style.position = 'static';
dummyBtn.style.position = 'absolute';
dummyBtn.style.left = mouseX;
dummyBtn.style.top = mouseY;
if ( dummyBtn.tagName == 'BUTTON' )
{
dummyBtn.innerHTML = currentButton.innerHTML;
}
else //INPUT
{
if ( dummyBtn.type == 'image' )
{
//Changing Image to Disable image.
//must have file 'add_disable.gif','deleteImage_disable.jpg'
//in directory structure
//if actual image button url is 'add.gif', 'deleteImage.jpg' respectively.
var extensiondot = currentButton.src.lastIndexOf('.');
dummyBtn.src = currentButton.src.substr(0,extensiondot) + '_disable'+
currentButton.src.substr(extensiondot);
}
}
dummyBtn.style.display = '';
}
function ReEnableAllButtons(objId)
{
//Image Button Object which fired the event.
var obj = document.getElementById(objId);
if ( idOfButtonsToDisable != '' )
{
var buttonIds = idOfButtonsToDisable.split(',');
for (var i=0; i< buttonIds.length; i++)
{
var btnClone = document.getElementById(buttonIds[i]+'_clone');
if ( btnClone != null )
btnClone.removeNode(true);
document.getElementById(buttonIds[i]).style.position='';
}
idOfButtonsToDisable = '';
}
if ( idOfImageButtonsToDisable != '' )
{
var imageButtonIds = idOfImageButtonsToDisable.split(',');
for (var i=0; i< imageButtonIds.length; i++)
{
var imgbtnClone = document.getElementById(imageButtonIds[i]+'_clone');
if ( imgbtnClone != null )
imgbtnClone.removeNode(true);
document.getElementById(imageButtonIds[i]).style.position='';
}
idOfImageButtonsToDisable = '';
}
}
//Call this function, if you want Enable the image before the time.
function EnableCustomButton(objId)
{
ReEnableAllButtons(objId);
}
</script>
";
Page.RegisterClientScriptBlock("CustomButtonScript",CustomButtonScript);
}
base.OnPreRender (e);
#endregion
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
#region Add disabling script on onclick event
string existingOnClick = "";
if ( this.Attributes["onclick"] != null )
existingOnClick = this.Attributes["onclick"];
//script which disables all buttons on the page.
string disableScript = "CustomButtonClicked(this);";
string concatwith = (existingOnClick.TrimEnd(' ').EndsWith
(";")==true||existingOnClick=="")?"":";";
if ( existingOnClick != "" )
{
existingOnClick = existingOnClick.Trim();
existingOnClick = existingOnClick.Replace("javascript:","");
if ( PutDisableScriptAtEnd == true )
disableScript = existingOnClick + concatwith + disableScript;
else
disableScript = disableScript + existingOnClick;
}
//Remove already being used "onclick", otherwise this attribute comes twice.
this.Attributes["onclick"]=null;
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, disableScript);
base.AddAttributesToRender(writer);
#endregion
}
}
常见问题解答
您是否强迫我在页面上只使用这个控件,而不能使用任何其他自定义控件或 HTML 控件?
完全不会。最终起作用的是 JavaScript。因此,您甚至可以将其复制到您的Global.js文件中,并调用其方法进行禁用。但我仍然认为最好将所有内容都放在控件内部,您可能需要创建类似的 ImageButton
,您可以按照下面的“未来增强”部分中的说明复制代码,这样您就不必在每个地方专门调用 JavaScript 方法了,例如,除了 CustomImageButton
,还可以使用 CustomButton
。
您能建议我可以在哪里使用它吗?
- 当您只想让用户单击一次按钮时,单击两次按钮可能会导致问题。
- 当您认为服务器上正在进行一些耗时的逻辑,并且系统看起来很慢或挂起,并且您不希望用户不耐烦地单击同一个按钮两次时。
此代码在 .NET 2.0 或更高版本中有效吗?
我不知道,因为我没有 .NET 2.0 可以测试。顺便说一句,是 JavaScript 在起作用,所以您可以复制 JavaScript 代码。
可能的未来增强
- 当前的自定义控件是用于
ImageButton
的。同样的代码也可以用于自定义Button
,即CustomButton
,它继承自System.Web.UI.WebControls.Button
。您需要将这里所有公开的属性复制到那里,并覆盖OnPreRender
和AddAttributesToRender
方法。 - 我们可以添加一个额外的属性
StatusMessage
,它可以在按钮被单击并禁用后,在处理过程中显示一些消息在状态栏中。
参考文献
没有参考文献,所有代码都是我从零开始编写的!:)
结论
我非常想知道此代码是否以任何方式帮助了您,请不要犹豫或懒惰地留下您的评论,告诉您对这次提交的感受以及它帮助了您多少。如果您在使用此控件的代码中保留此页面的 URL,我将不胜感激。
历史
- 2006年8月4日:初始版本
许可证
本文没有明确的许可附加,但可能包含文章文本或下载文件中的使用条款。如有疑问,请通过下方的讨论区联系作者。作者可能使用的许可证列表可以在此处找到。