jQuery.change + inline onchange fn() кара fn да се изпълни два пъти

Публикувах това, въпреки че намерих отговора и заобиколното решение, с надеждата, че ще помогне на някой друг.

По принцип, когато използвате IE8 и променяте стойността на елемент, който има едновременно jQuery.change закачен към него и дефиниран inline 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; премахването на предупреждението или връщането на bool, или дори превръщането на манипулатора в напълно празен няма влияние върху поведението на двойната промяна/постбек.

Също така се случва, независимо дали свързвате обработчика на събития TextChanged от страна на сървъра декларативно в маркиране или императивно в код (очевидно не трябва да правите и двете или сами ще си причините двойното обратно изпращане).

Въпреки че генерираният от ASP.NET onchange="__doPostBack(...)" се изпълнява два пъти, манипулаторът jQuery.change се изпълнява само веднъж, защото предупреждението се показва само веднъж. От време на време (може би 1 от 5 или 6 преминавания), onchange се задейства само един път, публикувайки обратно веднъж, така че грешката не изглежда да се проявява постоянно.

Още по-лошо е, че в реалния производствен проект, където за първи път се сблъскахме с това поведение, понякога, когато то удвоява публикации, IsPostBack=true за една заявка и false за другата, карайки кода за инициализация да се изпълни отново. Но не успях да възпроизведа това поведение в демонстрационния проект, така че може да е отделен проблем при взаимодействие с този.

Ако коментирате jQuery.change, той се връща към нормалното желано поведение при единично обратно изпращане.

Търсих в Google като луд, но успях да намеря само една публикация във форума, която МОЖЕ да е изправена пред същия проблем. Едва след като установих, че проблемът не е възникнал във Firefox и добавих IE8 към моя списък с думи за търсене, срещнах докладите за грешки в jQuery. http://dev.jquery.com/ticket/6310 http://dev.jquery. com/ticket/6593

Заобиколното решение (в ASP.NET) е да деактивирате AutoPostBack и изрично да добавите __doPostBack в края на jQuery манипулаторите на събития за всички контроли, които трябва да изпълняват както клиентски, така и сървърен код.

РЕДАКТИРАНЕ: Поправен е селектора на jQuery, в който, както посочи meep, липсва #.


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 контроли, не използвайте inline 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 по този начин. Благодаря, bobince! - person wonkim00; 04.10.2010