Есть панель, на ней текстбоксы. Есть javascript, который обрабатывает пользовательский ввод в текстбоксы, позволяя вводить только числа. То есть подписываемся на onKeyPress и возвращаем false, если юзер ввёл неразрешённый символ. При этом событие отменяется и символ не вводится.
Всё работало хорошо до какого-то момента, а потом работать перестало. Проблема была в Internet Explorer, в других целевых браузерах (Opera, Firefox, Chrome) косяк не проявлялся.
После долгих раскопок выяснил, что javascript-обработка сломалась после того, как стало использоваться свойство DefaultButton у родительской панели.
По сути, разрабатывалось что-то типа визарда (естественно, намного сложнее и наворочаннее, чем в примере, который тут приводится). Есть несколько шагов-"диалоговых окон", каждый из которых содержит некие элементы ввода/отображения данных и одну или несколько командных кнопок - "да", "нет", "далее" и т. п. Захотелось, чтобы, если пользователь, закончив ввод данных в текстбоксы, нажмёт <Enter>, происходили бы действия, соответствующие щелчку на дефолтной кнопке, например "далее". В ASP.Net есть решение "из коробки" - свойство DefaultButton у панели. Надо просто присвоить ему идентификатор нужной кнопки.
Так вот, после того, как это было сделано, перестало отменяться событие onKeyPress у текстбокса (обработчик вызывается, отрабатывает но возврат false при вводе запрещённого символа ни к чему не приводит - символ вводится).
Связано это, по-видимому, с тем, что родительская панель с заданным свойством DefaultButton сама обрабатывает onKeyPress, отлавливая нажатие Enter. Проблема, напомню, имеет место только в IE.
Возможные решения
1) Подписываться не на onKeyPress, а на onKeyDown. Этот вариант плох тем, что в event.keyCode будет не код символа, а код клавиши. Обработка получается более кривой, поскольку, например, при вводе "3" и "#" (Shift + 3) генерится один и тот же код, потому что клавиша одна, аналогично одинаковый код генерится при вводе русской точки и английского слеша, которые тоже на одной клавише (какой символ вводится - зависит от текущей раскладки клавиатуры). Я сначала пошёл по этому пути, но так и не добился требуемого результата, хотя, может быть, это и возможно.
2) В обработчике onKeyPress запретить событию всплывать, присвоив event.cancelBubble значение true. Это я и сделал в моём случае. Надо только не забыть проверить, какой символ введён, и, если это 13-ый символ (<Enter>), ничего не делать, позволить всплытие, чтобы наша панель с установленным свойством DefaultButton могла перехватить это событие и "щёлкнуть" на дефолтной кнопке.
if (code == 13) return; else event.cancelBubble = true;3) Ну и ещё один путь - отказаться от использования свойства DefaultButton панели и реализовывать этот функционал самостоятельно (ловить нажатия <Enter> и делать что нужно в этом случае).
Неоценимую помощь осознании причин проблемы оказал вот этот пост (англ.), найденный с помощью небезызвестной поисковой системы.
Простой примерчик, для иллюстрации.
Здесь злополучное свойство DefaultButton устанавливается программно у панели pWizard - ему присваивается идентификатор одной из кнопок, находящихся на показываемой в текущий момент вьюшке (см. CodeBehind). Каждая вьюшка - шаг визарда.
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="BugSample.aspx.cs"
Inherits="Amikko.BugSample" %>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Title</title>
<script type="text/javascript" language="javascript">
function UnsignedIntValidator(sender, e) {
var code = e.keyCode;
// Решение проблемы с onkeypress.
// Если не <Enter>, то запрещаем "всплытие" события.
if (code == 13)
return;
else
e.cancelBubble = true;
return code >= 48 && code <= 57
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="scriptManager" runat="server"
EnableScriptGlobalization="true" />
<asp:UpdatePanel runat="server" ID="upWizard"
RenderMode="Inline" UpdateMode="Conditional">
<ContentTemplate>
<asp:Panel ID="pWizard" runat="server">
<asp:MultiView ID="mvSteps" runat="server">
<asp:View ID="vViewFirst" runat="server">
<div>
Вьюшка vViewFirst<br />
Текстбокс, позволяющий вводить только числа<br />
<asp:TextBox ID="tbNumber" runat="server"
onkeypress="return UnsignedIntValidator(this, event ? event : window.event);" />
</div>
<asp:Button ID="bButton1" runat="server"
Text="Кнопка 1" />
<asp:Button ID="bButton2" runat="server"
Text="Кнопка 2" />
<asp:Button ID="bDefault" runat="server"
Text="Кнопка по умолчанию"
onclick="bDefault_Click" />
</asp:View>
<asp:View ID="vViewSecond" runat="server">
<div>
Вьюшка vViewSecond<br />
Какие-то контролы...<br />
<asp:TextBox ID="TextBox1" runat="server" /><br />
<asp:CheckBox ID="CheckBox1" runat="server" />
</div>
<asp:Button ID="bBack" runat="server"
Text="< Назад" onclick="bBack_Click" />
</asp:View>
</asp:MultiView>
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
CodeBehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Amikko
{
public partial class BugSample : System.Web.UI.Page
{
/// <summary>
/// Шаги
/// </summary>
private enum Steps
{
/// <summary>
/// Соответствует вьюшке vViewFirst
/// </summary>
First = 0,
/// <summary>
/// Соответствует вьюшке vViewSecond
/// </summary>
Second = 1,
}
/// <summary>
/// Определяет идентификатор кнопки,
/// нажатие на которую должно быть вызвано при
/// нажатии клавиши Enter
/// </summary>
/// <returns>Идентификатор дефолтной кнопки</returns>
private string GetDefaultButtonForCurrentStep()
{
var step = (Steps)mvSteps.ActiveViewIndex;
switch (step)
{
case Steps.First:
return bDefault.ID;
case Steps.Second:
return bBack.ID;
default:
return string.Empty;
}
}
private void InitDefaultButton()
{
pWizard.DefaultButton =
GetDefaultButtonForCurrentStep();
pWizard.Focus();
}
/// <summary>
/// Переход к другому шагу
/// Отображается соответствующая View
/// </summary>
/// <param name="newStep">Шаг создания заказа.</param>
private void ChangeStepTo(Steps newStep)
{
mvSteps.ActiveViewIndex = (int)newStep;
InitDefaultButton();
}
protected void Page_Load(object sender, EventArgs e)
{
ChangeStepTo(Steps.First);
}
protected void bDefault_Click
(object sender, EventArgs e)
{
ChangeStepTo(Steps.Second);
}
protected void bBack_Click(object sender, EventArgs e)
{
ChangeStepTo(Steps.First);
}
}
}
Комментариев нет:
Отправить комментарий