Atraso na chamada ajax no campo de texto para permitir a digitação

8

Gostaria de adiar o disparo do ajax da mesma maneira que o preenchimento automático parece funcionar. Por exemplo, se um usuário digitar, o ajax não será executado até que 500ms tenham decorrido desde a última instalação.

Atualmente, estou olhando para drupal.behaviors, mas não consigo fazê-lo funcionar.

Drupal.behaviors.mymodule = {
  attach: function(context, settings) { 
    $('input.andtimer', context).delay(500).ajaxStart();
  }
};

Este é o elemento do formulário ao qual o comportamento está anexado.

$form['my_input'] = array(
  '#type' => 'textfield',
  '#default_value' => $value,
  '#ajax' => array(
    'callback' => 'my_callback',        
    'event' => 'keyup',
    'wrapper' => 'my_wrapper',  
    'trigger_as' => array(
      'name' =>  'my_button',
  ),
  'progress' => array('type' => 'none'),
  ),
  '#attributes' => array(
    'class' => array('andtimer'),
  ),                      
);

Este jsfiddle mostra o que estou tentando alcançar.

Would Como substituir Drupal.ajax.prototype.beforeSend? ser um caminho para derrubar isso?

O seguinte funciona para o primeiro 'conjunto' de entradas com a classe .andtimer. Não está funcionando para nenhum outro conjunto, o ajax sempre continua com o primeiro conjunto. Alguma idéia de como corrigir isso?

(function($, Drupal) {
    Drupal.behaviors.bform = {
        attach : function(context, settings) {

            var events = $('.andtimer').clone(true).data('events');
            $('.andtimer').unbind('keyup');
            var typingTimer;
            var doneTypingInterval = 300;
            $('.andtimer').keyup(function() {
                clearTimeout(typingTimer);
                typingTimer = setTimeout(doneTyping, doneTypingInterval);
                function doneTyping() {
                    $.each(events.keyup, function() {
                        this.handler();
                    });
                }

                return false;
            });
        }
    };
})(jQuery, Drupal); 

Usando $ form ['my_input'] ['# ajax'] ['event'] = 'finishedinput' conforme sugerido e

var typingTimer;
var doneTypingInterval = 600;

$('.andtimer').on('keyup', function (e) {
  clearTimeout(typingTimer);
  if ($(this).val) {
    var trigid = $(this);
    typingTimer = setTimeout(function(){                    
      trigid.triggerHandler('finishedinput');
    }, doneTypingInterval);
  }
});

Trabalha para cada 'grupo' de insumos onde é necessário obter a contagem de insumos preenchidos.

Inigo Montoya
fonte
Esse código não tem nada a ver com keyup / keydown ou com a ligação de evento que você está aludindo - você pode adicionar seu código real, por favor? Lembre-se de que, se você está apenas procurando ajuda geral sobre javascript, este não é o lugar para encontrá-la. A regra é: fazê-lo funcionar fora do Drupal primeiro e, se você não conseguir que ele funcione dentro do Drupal, pergunte aqui
Clive
Obrigado Clive, adicionei o código para criar a entrada. Fui direto para a tentativa e fazê-lo funcionar em Drupal. Ainda aprendendo. Vou dar uma olhada lá fora e ver se não consigo esclarecer um pouco mais o problema.
Inigo Montoya
Eu falei cedo demais, não percebi o quão ligado a Drupal você está tentando fazer isso. Isso faz com que um problema bastante interessante :)
Clive
11
O trecho de código inferior funciona bem para mim, exceto após disparar o evento, o campo perde o foco. Como posso fazer isso é para que o foco permaneça no elemento após o disparo.
VanD 02/07/2015

Respostas:

7

Uma opção é usar um evento jQuery personalizado, por exemplo. algo como entrada acabada . Defina $form['my_input']['#ajax']['event'] = 'finishedinput'e forneça algumas JS para acionar seu evento personalizado após um atraso apropriado (semelhante à JS no violino).

Andy
fonte
Surpreendente ! Eu estava exatamente procurando por isso :) Muito obrigado. No entanto, se eu pudesse ter uma dica de como aplicar isso quando você alterar uma entrada de um formulário exposto de visualizações. Se eu não definir o retorno de chamada para $ form ['my_input'] ['# ajax'] nada acontecer, se eu adicionar as visualizações de envio, o formulário exposto submit fn como retorno de chamada (ou qualquer outra coisa) funcionará, mas retornará o índice indefinido: form_build_id. .. E eu não sei nem como e onde adicionar um $ form_state ['rebuild'] = TRUE Agradecemos antecipadamente
Kojo
11
@Kojo Você poderia fazer uma nova pergunta sobre isso e incluir descrições das configurações do Views AJAX, do filtro exposto, de qualquer código personalizado usado e dos problemas que ocorrem? O envio automático do CTools já possui um atraso (codificado) para o envio de campos de texto de 0,5s ( consulte auto-submit.js ).
217 Andy
Ah, ótimo, aqui está o cenário! Não é possível modificá-lo "on the fly" para uma entrada específica? Se não for simples, vou postar uma pergunta :) Obrigado pela ajuda!
Kojo
11
@ Kojo Não é tão fácil quanto deveria ser! Se fosse eu, provavelmente criaria meu próprio auto-submit.js e o usaria hook_js_alter()para garantir que ele seja usado no lugar do original. (Mas realmente imho os ctools código deve usar Drupal.settings em vez de um valor hard-coded.)
Andy
11
@Kojo Também dê uma olhada drupal.org/node/2023705 que é sobre como melhorar a experiência autosubmit (não apenas para entradas de texto). Há um patch que pode ser suficiente para você. EDIT: e se você tentar usá-lo, não se esqueça de colocar um comentário sobre o problema, dizendo se funcionou.
217 Andy
0

Essa abordagem tem a vantagem e a desvantagem de ser aplicada a todos os eventos AJAX acionados por keyup em qualquer página em que esse script seja executado.

!function ($) {
  const originalMethod = Drupal.ajax.prototype.eventResponse,
        timeoutDelay   = 500;

  var timeoutId;

  // Override the default event handler
  Drupal.ajax.prototype.eventResponse = function (element, event) {
    const self = this;
    clearTimeout(timeoutId);

    if ('keyup' === this.event) {
      // Fire the original event handler with a delay
      timeoutId = setTimeout(function (element, event) {
        originalMethod.apply(self, [element, event]);
      }, timeoutDelay, element, event);
    }
    else {
      // Fire the original event handler immediately
      originalMethod.apply(this, [element, event]);
    }
  };
}(jQuery);
tvanc
fonte
-1

Este é o código que escrevi:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
<head><title>Submit after typing finished</title>
<script language="javascript" type="text/javascript">
function DelayedSubmission() {
    var date = new Date();
    initial_time = date.getTime();
    if (typeof setInverval_Variable == 'undefined') {
            setInverval_Variable = setInterval(DelayedSubmission_Check, 50);
    } 
}
function DelayedSubmission_Check() {
    var date = new Date();
    check_time = date.getTime();
    var limit_ms=check_time-initial_time;
    if (limit_ms > 800) { //Change value in milliseconds
        alert("insert your function"); //Insert your function
        clearInterval(setInverval_Variable);
        delete setInverval_Variable;
    }
}

</script>
</head>
<body>

<input type="search" onkeyup="DelayedSubmission()" id="field_id" style="WIDTH: 100px; HEIGHT: 25px;" />

</body>
</html>
Alfredo Lingoist Jr.
fonte
3
Bem-vindo ao Drupal Answers. Você pode melhorar sua resposta escrevendo um pequeno parágrafo explicando o que o cide faz e como ele resolverá o problema dos POs.
Free Radical
-1

Eu também tentei "beforeSend" sem muita sorte. Eu então tropecei no "beforeSubmit" e isso faz o truque para mim. Você pode usar esta estratégia para conectar-se a outros métodos de protótipo Drupal ajax (consulte /misc/ajax.js para todos os métodos originais):

(function($, Drupal) {
    var delayedTimeoutId;
    var delayInterval = 500;

    /**
     * Modify form values prior to form submission.
     */
    Drupal.ajax.prototype.original_beforeSubmit = Drupal.ajax.prototype.beforeSubmit;
    Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
        // Some console stuff for info purposes:
        if(window.console) {
            console.log('beforeSubmit args:');
            console.log(this); // contains stuff like PHP AJAX callback, triggering selector, etc.
            console.log(form_values); // the form data
            console.log(element); // the triggering element
            console.log(options); // ajax options
        }

        // If it is the triggering selector or callback I want to delay, then do the delay:
        if(this.selector == '#my-text-input-id' || this.callback == '_my_module_ajax_callback') {
            // Clear timeout if it exists;
            clearTimeout(delayedTimeoutId);
            // Start waiting:
            delayedTimeoutId = setTimeout(function(drupalAjax, form_values, element, options) {
                delayedTimeoutId = null;
                // Execute original beforeSubmit:
                drupalAjax.original_beforeSubmit(form_values, element, options);
            }, delayInterval, this, form_values, element, options)
        } else {
            // Continue with original beforeSubmit:
            this.original_beforeSubmit(form_values, element, options);
        }
    };
}(jQuery, Drupal));
jduhls
fonte
Drupal.ajax.prototype.beforeSubmit é uma função vazia por padrão, portanto, esse código não faz nada. Invoca apenas um vazio com ou sem atraso.
Tv18
RE: função vazia - isso é verdade. Está vazio por enquanto. Mas a API Drupal pode mudar e pode não estar mais vazia. Esta é uma substituição segura. Além disso, atualmente não faz nada porque é responsabilidade do OP inserir os valores que eles desejam substituir.
jduhls
Drupal diz até no código para substituir esta função: / ** * Modifique os valores do formulário antes do envio do formulário. * / Drupal.ajax.prototype.beforeSubmit = function (valores_forma, elemento, opções) {// Esta função é deixada em branco para facilitar a substituição de módulos // que desejam adicionar funcionalidade aqui. };
jduhls