jQuery.change + встроенный onchange fn() заставляет fn выполняться дважды

Публикация этого, хотя я нашел ответ и обходной путь, в надежде, что это поможет кому-то еще.

По сути, при использовании IE8 и изменении значения элемента, к которому подключен как jQuery.change, так и определенный встроенный onchange, код в onchange выполняется дважды.

Мы столкнулись с этим в проекте ASP.NET (3.5 или 4.0 не имеет значения) с элементами управления jQuery 1.4.2 и AutoPostBack=true, которые отображаются с атрибутами onchange="__doPostBack(...)", в результате чего обратная передача выполняется дважды для одного изменения состояния управления, инициированного пользователем. (Microsoft действительно нужно выйти из бизнеса браузеров — у них это ужасно получается.)

Чтобы продемонстрировать проблему, я создал пустой проект веб-сайта и добавил одну страницу aspx, полный код + разметку ниже. Важными элементами являются ссылка SCRIPT на jQuery, $document.ready, который связывает обработчик control.change, и единственный элемент управления автоматической обратной передачей (я убедился, что это происходит как с TextBox, так и с DropDownList). Обратите внимание, что на странице нет других элементов управления — нет элементов управления AJAXToolkit, таких как UpdatePanels, и нет IMG с пустыми атрибутами SRC, что является давней ошибкой, которая также вызывает двойную обратную передачу.

<%@ Page Language="C#" AutoEventWireup="true" EnableEventValidation="false" 
    EnableSessionState="False" EnableViewState="false" %>

<script runat="server">
    protected void Page_Init(object sender, EventArgs e)
    {
        Trace.Write(String.Format("IsPostBack = {0}; PostbackControl = {1}",      
          IsPostBack.ToString(), Page.Request.Params.Get("__EVENTTARGET")));
    }

    protected void txtTest_TextChanged(object sender, EventArgs e)
    {
        Trace.Write(String.Format("Name = {0}", txtTest.Text));
    }
</script>

<html>
  <head>
    <title>Demo of jQuery + AutoPostBack double postback problem</title>
  </head>
  <body>
    <form id="Form1" runat="server">

      <script src="jquery-1.4.2.js" type="text/javascript"></script>
      <script type="text/javascript">
          $(document).ready(function () {
              $('#<%= txtTest.ClientID %>').change(function () { 
                alert('Double postback coming up!'); 
              })
          });
      </script>

      <asp:TextBox ID="txtTest" runat="server" AutoPostBack="true" 
        OnTextChanged="txtTest_TextChanged" />

    </form>
  </body>
</html>

Вы можете наблюдать двойную обратную передачу, либо установив точку останова в Page_Init, включив отладку сценария IE и пройдясь по javascript, чтобы увидеть, как onchange выполняется дважды, либо включив трассировку и просмотрев два результирующих запроса в trace.axd.

Неважно, что вы поместите в обработчик jQuery.change; удаление предупреждения или его возвращение логического значения или даже полное опустошение обработчика не влияет на двойное поведение onchange/postback.

Это также происходит независимо от того, подключаете ли вы обработчик событий TextChanged на стороне сервера декларативно в разметке или императивно в коде (очевидно, вы не должны делать и то, и другое, иначе вы сами вызовете двойную обратную передачу).

Несмотря на то, что сгенерированный ASP.NET onchange="__doPostBack(...)" выполняется дважды, обработчик jQuery.change выполняется только один раз, поскольку предупреждение отображается только один раз. Иногда (может быть, 1 из 5 или 6 проходов) onchange срабатывает только один раз, отправляя обратно один раз, поэтому ошибка не проявляется постоянно.

Хуже того, в реальном производственном проекте, где мы впервые столкнулись с таким поведением, иногда при двойной публикации IsPostBack=true для одного запроса и false для другого, что приводит к повторному выполнению кода инициализации. Но мне не удалось воспроизвести это поведение в демонстрационном проекте, поэтому взаимодействие с этим может быть отдельной проблемой.

Если вы закомментируете jQuery.change, он вернется к нормальному желаемому поведению одиночной обратной передачи.

Я гуглил как сумасшедший, но смог найти только одно сообщение на форуме, которое МОГУТ столкнуться с той же проблемой. Только после того, как я определил, что проблема не возникает в Firefox, и добавил IE8 в список условий поиска, я наткнулся на отчеты об ошибках jQuery. http://dev.jquery.com/ticket/6310 http://dev.jquery. ком/билет/6593

Обходной путь (в ASP.NET) заключается в отключении AutoPostBack и явном добавлении __doPostBack в конце обработчиков событий jQuery для всех элементов управления, которые должны выполнять как клиентский, так и серверный код.

РЕДАКТИРОВАТЬ: исправлен селектор jQuery, в котором, как указал мип, отсутствовал #.


person wonkim00    schedule 02.10.2010    source источник
comment
$('‹%= txtTest.ClientID %›').change(function () { Должно быть $('#‹%= txtTest.ClientID %›').change(function () {   -  person janhartmann    schedule 02.10.2010
comment
Хороший улов! Исправляем сейчас.   -  person wonkim00    schedule 04.10.2010


Ответы (2)


Насколько я знаю, эта проблема ограничена Internet Explorer, поэтому, если у вас есть возможность, уберите IE от пользователей вашего приложения.

Или отключите AutoPostBack и явно добавьте __doPostBack в конце обработчиков событий jQuery для всех элементов управления, которые должны выполнять как клиентский, так и серверный код. Если вы используете элементы управления на основе простого старого HTML, не используйте встроенные onchange И jQuery.change для одних и тех же элементов.

Или взломайте jQuery самостоятельно, чтобы onchange не выполнялся дважды, когда изменение перехватывается.

Или дождитесь следующего выпуска jQuery и надейтесь, что проблема решена.

Microsoft, скорее всего, никогда не пошевелит пальцем, чтобы исправить это, поскольку они продолжают распространять старые ошибки IE6 в более новые версии IE.

person wonkim00    schedule 02.10.2010

Помимо избиения Microsoft, это больше вина jQuery, чем IE. Это вызвано попыткой jQuery обойти отсутствие всплытия для события change в IE, вызывая дополнительные поддельные события change для каждого элемента-предка, а также для целевого элемента в дополнение к событию естественного изменения. Предположительно ошибка осталась незамеченной, так как запуск нескольких событий onchange обычно безвреден.

(Это не ответ. Но тогда ваш вопрос не вопрос.)

person bobince    schedule 02.10.2010
comment
Нет, это не ответ, но он дает хорошее представление о том, ПОЧЕМУ jQuery взаимодействует с IE таким образом. Спасибо, бобинка! - person wonkim00; 04.10.2010