jQuery $ (документ) .ready и UpdatePanels?

475

Я использую jQuery для подключения некоторых эффектов наведения мыши на элементах внутри UpdatePanel. События связаны в $(document).ready. Например:

$(function() {    
    $('div._Foo').bind("mouseover", function(e) {
        // Do something exciting
    });    
});

Конечно, это работает нормально при первой загрузке страницы, но когда UpdatePanel выполняет частичное обновление страницы, он не запускается и эффекты наведения мыши больше не работают внутри UpdatePanel.

Каков рекомендуемый подход для подключения компонентов в jQuery не только при первой загрузке страницы, но и каждый раз, когда UpdatePanel запускает частичное обновление страницы? Должен ли я использовать жизненный цикл ASP.NET ajax вместо $(document).ready?

Херб Каудилл
источник
Попробуйте, я думаю, это решит вашу проблему codeproject.com/Articles/21962/…
Baxterboom
1
Аналогично этому вопросу: stackoverflow.com/questions/301473/…
Per Hornshøj-Schierbeck

Ответы:

545

UpdatePanel полностью заменяет содержимое панели обновления при обновлении. Это означает, что те события, на которые вы подписались, больше не подписываются, поскольку в этой панели обновлений есть новые элементы.

Чтобы обойти это, я переподписывался на события, которые мне нужны после каждого обновления. Я использую $(document).ready()для начальной загрузки, затем использую Microsoft PageRequestManager(доступно, если у вас есть панель обновлений на вашей странице), чтобы переподписывать каждое обновление.

$(document).ready(function() {
    // bind your jQuery events here initially
});

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_endRequest(function() {
    // re-bind your jQuery events here
});

Это PageRequestManagerобъект javascript, который автоматически доступен, если на странице есть панель обновления. Вам не нужно ничего делать, кроме приведенного выше кода, чтобы использовать его, пока панель обновления находится на странице.

Если вам нужен более подробный контроль, это событие передает аргументы, аналогичные тому, как события .NET передаются аргументы, (sender, eventArgs)так что вы можете видеть, что вызвало событие, и выполнять только повторную привязку, если это необходимо.

Вот последняя версия документации от Microsoft: msdn.microsoft.com/.../bb383810.aspx


Лучший вариант, который вы можете иметь, в зависимости от ваших потребностей, это использовать jQuery .on(). Эти методы более эффективны, чем повторная подписка на элементы DOM при каждом обновлении. Однако, прежде чем использовать этот подход, прочитайте всю документацию, поскольку она может соответствовать или не соответствовать вашим потребностям. Существует множество плагинов jQuery, которые было бы неразумно использовать для рефакторинга .delegate()или .on(), поэтому в этих случаях вам лучше переподписаться.

Dan Herbert
источник
1
Пожалуйста, будьте более точны, когда вы даете объяснение. Я имею в виду пример использования гораздо лучше, чем пара разбросанных строк кода. См. Объяснение Брайана Маккея. Идеально!
искатель истины
3
@Dan, по состоянию на jQuery 1.7, .delegate()был заменен.on()
Габриэле Петриоли
@ GabyakaG.Petrioli Я понимаю, что он был заменен, но поскольку этот ответ, похоже, решает общую проблему, я хотел добиться максимальной совместимости со старыми версиями jQuery, которые еще не были обновлены. Мой ранее рекомендованный ответ .live(...)был официально объявлен устаревшим в 1.7 и может быть удален в следующей версии. Я намеренно выбрал, .delegate()поскольку он уже некоторое время поддерживается в jQuery и вряд ли будет удален в ближайшее время. Тем не менее, я уточню свой ответ, чтобы упомянуть .on()и тех, кто может его использовать.
Дэн Герберт
12
Лучше объявить и подключить переменную prm внутри $ (document) .ready, поскольку возможно, что Sys еще не был объявлен (в зависимости от того, где находится скрипт).
drake7707
1
@AhmedSamy Не рекомендуется использовать jQuery.delegate()больше. Он был заменен тем, jQuery.on()который является предпочтительным API для использования. delegate()это просто обертка для конкретного использования on(), и вполне возможно, что в будущем это может устареть. Мой предыдущий комментарий delegate()был написан более года назад, когда on()был выпущен jQuery 1.7 (который представил API). Поскольку jQuery 1.9 уже выпущен и 2.0 в бета-версии готовится к выпуску, рекомендуется избегать использования delegate()в любом новом коде.
Дэн Герберт
159
<script type="text/javascript">

        function BindEvents() {
            $(document).ready(function() {
                $(".tr-base").mouseover(function() {
                    $(this).toggleClass("trHover");
                }).mouseout(function() {
                    $(this).removeClass("trHover");
                });
         }
</script>

Площадь, которая будет обновляться.

<asp:UpdatePanel...
<ContentTemplate
     <script type="text/javascript">
                    Sys.Application.add_load(BindEvents);
     </script>
 *// Staff*
</ContentTemplate>
    </asp:UpdatePanel>
Барбарос Алп
источник
Хорошо работает для asp: GridView TextBox внутри <EditItemTemplate>
Дэн Рэндольф
Проблема с этим решением заключается в том, что, хотя оно работает, оно убивает асинхронный пост UpdatePanel, снова делая UpdatePanel бесполезным. (вздох)
MC9000
63

Пользовательский элемент управления с jQuery внутри UpdatePanel

Это не прямой ответ на вопрос, но я соединил это решение, прочитав ответы, которые нашел здесь, и подумал, что кто-то может найти его полезным.

Я пытался использовать ограничитель текстовой области jQuery внутри пользовательского элемента управления. Это было сложно, потому что пользовательский элемент управления работает внутри UpdatePanel, и он терял свои привязки при обратном вызове.

Если бы это была просто страница, ответы здесь были бы применены напрямую. Однако пользовательские элементы управления не имеют прямого доступа к тегу head и не имеют прямого доступа к UpdatePanel, как предполагают некоторые ответы.

Я закончил тем, что поместил этот блок сценария прямо в разметку моего пользовательского элемента управления. Для начального связывания он использует $ (document) .ready, а затем использует prm.add_endRequest оттуда:

<script type="text/javascript">
    function BindControlEvents() {
        //jQuery is wrapped in BindEvents function so it can be re-bound after each callback.
        //Your code would replace the following line:
            $('#<%= TextProtocolDrugInstructions.ClientID %>').limit('100', '#charsLeft_Instructions');            
    }

    //Initial bind
    $(document).ready(function () {
        BindControlEvents();
    });

    //Re-bind for callbacks
    var prm = Sys.WebForms.PageRequestManager.getInstance(); 

    prm.add_endRequest(function() { 
        BindControlEvents();
    }); 

</script>

Так что ... Просто подумал, что кто-то хотел бы знать, что это работает.

Брайан Маккей
источник
2
Я не мог заставить это работать на всю жизнь. Тогда я переместил это от головы до нижнего колонтитула, и это работало. Не позитивно, но я думаю, что это должно прийти после ScriptManager, который я использую.
Адам Янгерс
В моем случае я добавлял сценарий с помощью ScriptManager.RegisterClientScriptBlock, который добавляет сценарий до определения Sys, поэтому я вместо этого использовал RegisterStartupScript (см. Разницу здесь )
Firas Assaad
2
Классный ответ! это работало как очарование в моем веб-приложении asp.net. + для вас
Пранеш Джанартанан
33

Обновитесь до jQuery 1.3 и используйте:

$(function() {

    $('div._Foo').live("mouseover", function(e) {
        // Do something exciting
    });

});

Примечание: live работает с большинством событий, но не со всеми. Полный список в документации .

svinto
источник
Использование оперативного решения проблемы с панелями обновления. Никакие обручи не должны прыгать через.
Тим Скарборо
Как насчет того, что должно произойти при загрузке страницы? Например зебра чередование столов? $ ('TABLE TR: nth-child (odd)'). AddClass ('alt-row');
Адам Янгерс
3
Просто для обновления, поскольку в jQuery 1.7 теперь рекомендуется использовать вместо него .on ()
Джордж
1
Только что обнаружен серьезный сбой при использовании live()метода в IE7 (проект jQuery 1.5.1 / VS2010). При использовании live()внутри UpdatePanel в v3.5 ASP.NET регулярные AutoPostbackиз <asp:DropDownList />причин 2 частичные постбэк в отличие от ожидаемого 1. Дополнительного постбэка выданной браузер испытывает недостаток контента (прерванный) заставляя Page.IsPostBackбыть false(что убивает состояние внутри моей границы контроль). Я обнаружил, что виновником является метод live (). Я понимаю, что 1-й постбэк должен быть прерван UpdatePanel по спецификации, но только если в очереди есть еще один, которого нет.
timmi4sa
20

Вы также можете попробовать:

<asp:UpdatePanel runat="server" ID="myUpdatePanel">
    <ContentTemplate>

        <script type="text/javascript" language="javascript">
        function pageLoad() {
           $('div._Foo').bind("mouseover", function(e) {
               // Do something exciting
           });
        }
        </script>

    </ContentTemplate>
</asp:UpdatePanel>

, поскольку pageLoad () является ajax-событием ASP.NET, которое выполняется каждый раз, когда страница загружается на стороне клиента.

Даниэль Хурсан
источник
pageLoad дублирует события на каждой обратной передаче ASync панели обновления.
Зо
17

Мой ответ?

function pageLoad() {

  $(document).ready(function(){

и т.п.

Работал как шарм, где ряд других решений с треском провалились.

Jono
источник
Идеально подходит для изменения размера текстовых областей в моем Listview в моей UpdatePanel в моем UserControl в соответствии с количеством текста.
Ресурс
Я боролся за то, чтобы функции dom jquery работали корректно при обратной передаче с панелями обновления, это решение работало, и мне не приходилось дублировать вызовы функций в 2 сценариях. Это поддерживало функции jquery и слушателя. Примечание. Я размещаю все свои скрипты и библиотеки jquery прямо перед тегом FORM [end] внизу страницы. Спасибо!!!
moto_geek
7

Я бы использовал один из следующих подходов:

  1. Инкапсулируйте привязку события в функцию и запускайте ее каждый раз, когда вы обновляете страницу. Вы всегда можете содержать привязку события к конкретным элементам, чтобы не связывать события несколько раз с одними и теми же элементами.

  2. Используйте плагин livequery , который автоматически выполняет первый способ для вас. Ваше предпочтение может варьироваться в зависимости от степени контроля, который вы хотите иметь для привязки события.

Эран Гальперин
источник
7

Это сработало для меня:

$(document).ready(function() {

    // Do something exciting

    var prm = Sys.WebForms.PageRequestManager.getInstance();

    prm.add_endRequest(function() {
        // re-bind your jQuery events here
    });

});
Turbojohan
источник
6

Функция pageLoad () очень опасна для использования в этой ситуации. Вы можете сделать события проводными несколько раз. Я бы также держался подальше от .live (), так как он присоединяется к элементу документа и должен пересекать всю страницу (медленно и дрянно).

Лучшее решение, которое я видел до сих пор, - это использовать функцию jQuery .delegate () на оболочке за пределами панели обновления и использовать всплывающие окна. Кроме того, вы всегда можете подключить обработчики, используя библиотеку Microsoft Ajax, которая была разработана для работы с UpdatePanels.

Норма
источник
6

Когда $(document).ready(function (){...})не работает после пост страницы назад, используйте функцию JavaScript pageLoad в Asp.page следующим образом:

<script type="text/javascript" language="javascript">
function pageLoad() {
// Initialization code here, meant to run once. 
}
</script>
Прадип Подробнее
источник
Да, проверьте также isPartialLoad: stackoverflow.com/questions/301473/…
Стив Грин,
4

У меня была похожая проблема, и я нашел способ, который работал лучше всего, - это использовать Event Bubbling и делегирование событий для ее решения. Хорошая вещь о делегировании событий заключается в том, что после настройки вам не нужно перепривязывать события после обновления AJAX.

В моем коде я настраиваю делегата на родительском элементе панели обновлений. Этот родительский элемент не заменяется при обновлении, и, следовательно, привязка события не затрагивается.

Существует множество хороших статей и плагинов для обработки делегирования событий в jQuery, и эта функция, вероятно, будет включена в версию 1.3. Статья / плагин, который я использую для справки:

http://www.danwebb.net/2008/2/8/event-delegation-made-easy-in-jquery

Как только вы поймете, что происходит, я думаю, что вы найдете это гораздо более элегантное решение, которое будет более надежным, чем повторное связывание событий после каждого обновления. Это также дает дополнительное преимущество, так как дает вам одно событие для отмены привязки, когда страница выгружается.

Джо Бринкман
источник
4

FWIW, у меня возникла похожая проблема с MooTools. Повторное присоединение моих событий было правильным ходом, но это нужно было сделать в конце запроса .. например

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function() {... 

Просто что-то нужно иметь в виду, если beginRequest заставляет вас получать исключения JS с нулевой ссылкой.

ура


источник
3
pageLoad = function () {
    $('#div').unbind();
    //jquery here
}

Функция pageLoad идеально подходит для этого случая, так как она запускается при начальной загрузке страницы и каждой асинхронной обратной передаче панели обновления. Мне просто нужно было добавить метод unbind, чтобы jquery работал с постбэками updatepanel.

http://encosia.com/document-ready-and-pageload-are-not-the-same/

Дуэйн
источник
2

Мой ответ основан на всех комментариях экспертов, приведенных выше, но ниже приведен следующий код, который любой может использовать, чтобы убедиться, что при каждой обратной передаче и при каждой асинхронной обратной передаче все еще будет выполняться код JavaScript.

В моем случае у меня был пользовательский элемент управления на странице. Просто вставьте приведенный ниже код в свой пользовательский элемент управления.

<script type="text/javascript"> 
        var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_endRequest(EndRequestHandler);
    function EndRequestHandler(sender, args) {
        if (args.get_error() == undefined) {
            UPDATEPANELFUNCTION();
        }                   
    }

    function UPDATEPANELFUNCTION() {
        jQuery(document).ready(function ($) {
            /* Insert all your jQuery events and function calls */
        });
    }

    UPDATEPANELFUNCTION(); 

</script>
Абхишек Шривастава
источник
2

Панель обновления всегда заменяет ваш Jquery встроенными скриптами Scriptmanager после каждой загрузки. Лучше, если вы используете методы экземпляра pageRequestManager, как это ...

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(onEndRequest)
    function onEndRequest(sender, args) {
       // your jquery code here
      });

это будет работать нормально ...

Рохит Шарма
источник
Старый пост, но тот, который помог мне. Одна вещь, которую я хотел бы добавить к этому: расположение кода add_endRequest должно быть внутри UpdatePanel, так как Sys.WebForms.PageRequestManager доступен только в этот момент. Тем не менее, фактическая функция не должна быть в этом месте. Итак, в моем случае у меня есть файл script.js, который содержит код, который я хочу связать, содержащийся внутри функции. В этом файле script.js у меня есть вызов document.ready, который вызывает вышеупомянутую функцию. Затем в моей UpdatePanel у меня есть код add_endRequest, который также вызывает вышеуказанную функцию.
Киран Рамасвами
1

Используйте скрипт ниже и измените тело скрипта соответственно.

       <script>
        //Re-Create for on page postbacks
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(function () {
           //your codes here!
        });
    </script>
mzonerz
источник
0

В ответ на ответ Брайана Маккея:

Я вставляю JavaScript в свою страницу через ScriptManager вместо того, чтобы помещать его прямо в HTML-код UserControl. В моем случае мне нужно перейти к форме, которая станет видимой после того, как UpdatePanel завершит работу и вернется. Это идет в коде файла. В моем примере я уже создал переменную prm на главной странице содержимого.

private void ShowForm(bool pShowForm) {
    //other code here...
    if (pShowForm) {
        FocusOnControl(GetFocusOnFormScript(yourControl.ClientID), yourControl.ClientID);
    }
}

private void FocusOnControl(string pScript, string pControlId) {
    ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "focusControl_" + pControlId, pScript, true);
}

/// <summary>
/// Scrolls to the form that is made visible
/// </summary>
/// <param name="pControlId">The ClientID of the control to focus on after the form is made visible</param>
/// <returns></returns>
private string GetFocusOnFormScript(string pControlId) {
    string script = @"
    function FocusOnForm() {
        var scrollToForm = $('#" + pControlId + @"').offset().top;
        $('html, body').animate({ 
            scrollTop: scrollToForm}, 
            'slow'
        );
        /* This removes the event from the PageRequestManager immediately after the desired functionality is completed so that multiple events are not added */
        prm.remove_endRequest(ScrollFocusToFormCaller);
    }
    prm.add_endRequest(ScrollFocusToFormCaller);
    function ScrollFocusToFormCaller(sender, args) {
        if (args.get_error() == undefined) {
            FocusOnForm();
        }
    }";
    return script;
}
fujiiface
источник
0
Sys.Application.add_load(LoadHandler); //This load handler solved update panel did not bind control after partial postback
function LoadHandler() {
        $(document).ready(function () {
        //rebind any events here for controls under update panel
        });
}
Тони Донг
источник