Ближайшие элементы JQuery из TextBox в GridView

У меня есть эта функция javascript:

function modifica(txtValoreID, txtRicalcolatoID, lblDeltaID) {
  // Ricalcolo il valore aggiornato
  var Valore = parseFloat($("#" + txtValoreID).val()).toFixed(2);
  var Ricalcolato = parseFloat($("#" + txtRicalcolatoID).val()).toFixed(2);
  $("#" + lblDeltaID).html(parseFloat(Valore - Ricalcolato).toFixed(2).replace('.', ','));
}

Это относится к текстовому полю и метке в представлении сетки:

<asp:TemplateField> 
    <ItemTemplate>
        <asp:TextBox ID="txtValore" Width="90%" Text='<%# string.Format("{0:N}", Eval("Valore"))%>' runat="server"  />
    </ItemTemplate>
</asp:TemplateField>
<asp:TemplateField> 
    <ItemTemplate>
        <asp:TextBox ID="txtRicalcolato" Text='<%# string.Format("{0:N}", Eval("Ricalcolato")) %>' runat="server" />
    </ItemTemplate>
</asp:TemplateField>

(...)

<asp:TemplateField HeaderText="Delta"> 
    <ItemTemplate>
        <asp:Label ID="lblDelta" Text='<%# string.Format("{0:N}", Eval("Delta"))%>' runat="server" />
    </ItemTemplate>
</asp:TemplateField>

И я назначаю его в DataBound:

txtValore.Attributes.Add("onchange", "modifica('" + txtValore.ClientID + "', '" + txtRicalcolato.ClientID + "','" + lblDelta.ClientID + "')");
txtRicalcolato.Attributes.Add("onchange", "modifica('" + txtDaLiquidare.ClientID + "', '" + txtRicalcolato.ClientID + "','" + lblDelta.ClientID + "')");

и он работает отлично. Проблема в том, что когда в gridview много записей, привязка данных влияет на производительность, поэтому мой первый вопрос: улучшаю ли я эту производительность, перемещая назначение из кода сервера с привязкой данных на страницу aspx? И если да, то как я могу это сделать? Потому что я пытаюсь так:

asp:TextBox ID="txtValore" onchange="test(this.id)" Text='<%# string.Format("{0:N}", Eval("PremioLiquidare"))%>' runat="server"  />

и это:

function test(txtID) {
    alert('TEST ' + $(this));
    alert('TEST ' + $(this).val());
    alert('TEST ' + $("#" + txtID).val());
    alert('TEST ' + $("#" + txtID).find("[id*='txtRicalcolato']").val());
    alert('TEST ' + $("#" + txtID).closest("[id*='txtRicalcolato']").val());
    alert('TEST ' + $(this).find("[id*='txtRicalcolato']").val());
    alert('TEST ' + $(this).closest("[id*='txtRicalcolato']").val());    
}

Но первый печатает объект TEST Object, второй пустой и третий правильное значение, а остальные не определены. Что я делаю неправильно?

Визуализированный HTML:

<table class="mGrid" id="ctl00_MainContent_grid">
    <tbody>
        (...)
        <tr>
            (...)
            <td>
                <input name="ctl00$MainContent$grid$ctl03$txtValore" value="1.693,44" id="ctl00_MainContent_grid_ctl03_txtValore" onchange="test(this.id)" type="text">
            </td>
            <td>
                <input name="ctl00$MainContent$grid$ctl03$txtRicalcolato" value="169,34" id="ctl00_MainContent_grid_ctl03_txtRicalcolato" onchange="test(this.id)" type="text">
            </td>
            (...)
            <td>
                <span id="ctl00_MainContent_grid_ctl03_lblDelta">-1.524,10</span>
            </td>
            (...)
        </tr>
        (...)
    </tbody>
</table>

person MisterFrank    schedule 14.11.2014    source источник
comment
Вместо добавления обработчиков изменений на основе атрибутов используйте один делегированный обработчик событий, прикрепленный к неизменяемому элементу-предку (например, к окружающему table или div). Это имеет гораздо меньшие накладные расходы и сохраняет весь код в одном месте.   -  person Gone Coding    schedule 14.11.2014
comment
Как упоминалось в моем ответе, если вы можете привести пример фактического HTML-кода из браузера, а не исходного кода, я могу предоставить более конкретный пример. :)   -  person Gone Coding    schedule 14.11.2014


Ответы (1)


Вы пытаетесь искать элемент вниз с помощью find и элемент вверх с помощью closest, но целевой элемент находится в другой ветви DOM.

  • find ищет только от элемента вниз (его потомки)
  • closest ищет только предков (не буквально самое близкое совпадение).

Вам нужно искать вверху с помощью closest, затем find вниз:

например 3-й и 4-й должны быть:

    alert('TEST ' + $("#" + txtID).closest('someCommonParentElementSelector').find("[id*='txtRicalcolato']").val());

Предложение: используйте один делегированный обработчик событий

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

Основываясь на вашем новом примере HTML, делегированный обработчик событий будет выглядеть примерно так:

// Shortcut DOM ready handler
$(function(){
    // Listen for change events bubbling up to the grid, 
    // then match elements with ID ending in "txtValore"
    $('.mGrid').on('change', 'input[id$="txtValore"]' function(){

          // this is the *txtValore field that changed
          var $input = $(this);

          // Find the matching input by looking up (to closest row) and then down the tree
          // for an element with ID ending in "txtRicalcolato"
          var $otherInput = $input.closest('tr').find('input[id$="txtRicalcolato"]');

          // now do "something" with the two inputs :)
    });
});

Примечания:

  • Затем вы можете удалить любые атрибуты onChange из ваших входных данных.
  • Это упрощает настройку и помещает код обработчика событий вместе с регистрацией обработчика событий.
  • If there are no dynamically added elements, you could just use a jQuery handler, like
    • $('.mGrid input[id$="txtValore"]').on('change', function(){ or
    • $('.mGrid input[id$="txtValore"]').change(function(){, но делегированные события более гибкие.
  • селектор [id$=""] является селектором заканчивается, поскольку ваши фактические идентификаторы начинаются с префикса генерации.
  • Делегированный обработчик событий работает, прослушивая события, которые всплывают на неизменный элемент-предок (в данном случае с классом .mGrid), затем применяя селектор jQuery к элементам в цепочке пузырьков, вызвавшим событие, затем применяя функцию. Это работает и для динамически добавляемых элементов, поскольку селектор запускается во время события, а не во время регистрации события.

Обновление: изменено создание идентификатора

В комментарии вы упоминаете, что генерация идентификатора изменилась, так что суффикс больше не был в конце идентификатора (были добавлены подчеркивание и число). jQuery также имеет селектор attribute contains *=, который изменит код примера на:

// Shortcut DOM ready handler
$(function(){
    // Listen for change events bubbling up to the grid, 
    // then match elements with ID containing "txtValore"
    $('.mGrid').on('change', 'input[id*="txtValore"]' function(){

          // this is the *txtValore field that changed
          var $input = $(this);

          // Find the matching input by looking up (to closest row) and then down the tree
          // for an element with ID containing "txtRicalcolato"
          var $otherInput = $input.closest('tr').find('input[id*="txtRicalcolato"]');

          // now do "something" with the two inputs :)
    });
});
person Gone Coding    schedule 14.11.2014
comment
Благодарю вас! Я обновил вопрос. Я не понимаю, что вы имеете в виду. Окружая всю сетку в div или таблице? Или просто строка, содержащая элементы? - person MisterFrank; 14.11.2014
comment
ХОРОШО! Теперь я понял.. alert('TEST' + $(# + txtID).closest(tr).find([id*='txtRicalcolato']).val()); - person MisterFrank; 14.11.2014
comment
Но что вы подразумеваете под использованием одного делегированного обработчика событий? - person MisterFrank; 14.11.2014
comment
@MisterFrank: обновленный ответ для объяснения обработки делегированных событий применительно к вашему примеру. - person Gone Coding; 14.11.2014
comment
Мне очень нравится это решение, но я не могу заставить его работать... Пробовал также $(document).ready(function () { alert('Ready!!'); $(.mGrid).on(click, function ( ) { предупреждение('Чао'); }); }); и у меня странное поведение. У меня есть первая сетка с этим классом, она работает. Затем переходите на вторую страницу, с сеткой вопроса, того же класса, но не работает. Вернитесь на первую страницу, больше не работает. Из-за постбэка? - person MisterFrank; 14.11.2014
comment
P.S. Я перестал использовать селектор end-with, потому что при переходе в тестовую среду у меня было то же самое, что и MainContent_grid_txtValore_0.. И много времени было потрачено впустую, чтобы понять, почему не работает.. может быть, другой IIS.. - person MisterFrank; 14.11.2014
comment
Извините, я сделал ошибку, когда я говорю «Затем переход на вторую страницу», что я на самом деле имею в виду. Затем я нажимаю кнопку, которая скрывает первое представление сетки и показывает другое, с обратной передачей, но все еще на той же странице. - person MisterFrank; 14.11.2014
comment
@MisterFrank: Показанный пример должен быть здравым, но одна тривиальная ошибка может остановить что-то работающее (например, изменение генерации идентификатора). Существует такой селектор, как '[id*="txtValore"]', который будет соответствовать обоим показанным вами случаям. Сейчас самое время опубликовать новый вопрос со всем кодом, который вы пытаетесь выполнить, чтобы можно было исправить конкретный пример. Ваше здоровье :) - person Gone Coding; 14.11.2014
comment
@MisterFrank:: Пример теперь обновлен, чтобы учесть совпадение в любом месте атрибута ID. Если проблемы не исчезнут, задайте новый вопрос и киньте ссылку сюда. Спасибо :) - person Gone Coding; 14.11.2014
comment
Спасибо еще раз! Когда я говорю о селекторе end-with, я имею в виду, что именно поэтому я использовал его в исходном вопросе. stackoverflow.com/questions/26934872/ - person MisterFrank; 14.11.2014