Retrase la llamada ajax en el campo de texto para permitir escribir

8

Me gustaría retrasar el disparo de ajax de la misma manera que el autocompletado parece funcionar. Por ejemplo, si un usuario está escribiendo, el ajax no se ejecutará hasta que hayan transcurrido 500 ms desde el último keyup.

Actualmente estoy viendo drupal.behaviors pero no puedo hacer que funcione.

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

Este es el elemento de formulario al que se adjunta el comportamiento.

$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 muestra lo que estoy tratando de lograr.

¿ Cómo anular Drupal.ajax.prototype.beforeSend? ser una ruta para acabar con esto?

Lo siguiente funciona para el primer 'conjunto' de entradas con la clase .andtimer. No funciona para ningún otro conjunto, el ajax siempre continúa con el primer conjunto. Alguna idea de cómo solucionar este problema?

(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' como se sugiere y

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);
  }
});

Funciona para cada 'grupo' de entradas donde se necesita obtener el recuento de entradas llenas.

Iñigo Montoya
fuente
Ese código no tiene nada que ver con keyup / keydown o el enlace de evento al que te refieres. ¿Puedes agregar tu código real por favor? Tenga en cuenta que si solo está buscando ayuda general de JavaScript, este no es el lugar para encontrarla. La regla es: primero haga que funcione fuera de Drupal, y si no puede hacerlo funcionar dentro de Drupal, pregunte aquí
Clive
Gracias Clive, agregué el código para construir la entrada. Fui directamente al intento y lo hice funcionar en Drupal. Aún aprendiendo. Lo intentaré afuera y veré si no puedo aclarar el problema un poco más en mi cabeza.
Iñigo Montoya
Hablé demasiado pronto, no me di cuenta de cuán vinculado a Drupal estás tratando de hacer esto. Esto hace un problema bastante interesante :)
Clive
1
El fragmento de código inferior funciona bien para mí, excepto después de disparar el evento, el campo pierde el foco. ¿Cómo puedo hacer para que el foco permanezca en el elemento después de disparar?
VanD

Respuestas:

7

Una opción es usar un evento jQuery personalizado, por ejemplo. algo así como entrada terminada . Configure $form['my_input']['#ajax']['event'] = 'finishedinput'y proporcione algunos JS para activar su evento personalizado después de un retraso apropiado (similar al JS en el violín).

Andy
fuente
Asombroso ! Estaba exactamente buscando eso :) Muchas gracias. Sin embargo, si pudiera tener un consejo sobre cómo aplicar esto cuando modifique una entrada de un formulario de vistas expuestas. Si no configuro la devolución de llamada en $ form ['my_input'] ['# ajax'] no pasa nada, si agrego las vistas de envío expuestas en el formulario submit fn como devolución de llamada (o cualquier otra cosa) funciona pero devuelve un índice indefinido: form_build_id. .. Y no sé cómo y dónde agregar un $ form_state ['rebuild'] = VERDADERO Gracias de antemano
Kojo
1
@Kojo ¿Podría hacer una nueva pregunta sobre esto e incluir descripciones de la configuración de Vistas AJAX, el filtro expuesto, cualquier código personalizado que esté usando y los problemas que ocurren? Btw CTools autosubmit ya tiene un retraso (codificado) en el envío desde campos de texto de 0.5s ( ver auto-submit.js ).
Andy
Ah genial, aquí está el escenario! ¿No es posible modificarlo "sobre la marcha" para una entrada específica? Si no es simple, publicaré una pregunta :) ¡Gracias por su ayuda!
Kojo
1
@Kojo ¡No tan fácilmente como debería ser! Si fuera yo, probablemente crearía mi propio auto-submit.js y lo usaría hook_js_alter()para asegurarme de que se usa en lugar del original. (Pero realmente, en mi opinión, el código de ctools debería usar Drupal.settings en lugar de un valor codificado).
Andy
1
@Kojo También eche un vistazo a drupal.org/node/2023705, que trata de mejorar la experiencia de envío automático (no solo para las entradas de texto). Hay un parche que podría ser suficiente para ti. EDITAR: y si intentas usarlo, no olvides poner un comentario sobre el tema diciendo si funcionó.
Andy
0

Este enfoque tiene la ventaja y la desventaja de aplicarse a todos los eventos AJAX activados por keyup en cualquier página donde se ejecute este script.

!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
fuente
-1

Este es el código que escribí:

<!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.
fuente
3
Bienvenido a Drupal Answers. Puede mejorar su respuesta escribiendo un breve párrafo que explique lo que hace el cide y cómo resolverá el problema de los OP.
Radical libre
-1

También probé "beforeSend" sin mucha suerte. Luego me topé con "beforeSubmit" y eso me sirve. También puede usar esta estrategia para conectar otros métodos prototipo de Drupal ajax (consulte /misc/ajax.js para ver todos los métodos originales):

(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
fuente
Drupal.ajax.prototype.beforeSubmit es una función vacía por defecto, por lo que este código en realidad no hace nada. Simplemente invoca un vacío con o sin demora.
tvanc
RE: función vacía: esto es cierto. Está vacío por ahora. Pero la API de Drupal podría cambiar y es posible que ya no esté vacía. Esta es una anulación segura. Además, actualmente no hace nada porque depende del OP insertar los valores que deseen anular.
jduhls
Drupal incluso lo dice en el código para anular esta función: / ** * Modificar los valores del formulario antes del envío del formulario. * / Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {// Esta función se deja vacía para simplificar la anulación de los módulos // que desean agregar funcionalidad aquí. };
jduhls