heinetz: Funktion mit Parameter aufrufen

Hallo Forum,

ich habe hier eine JS-Funktion gefunden, die ich einsetzen möchte und experimentiere damit herum.

    const debounce = function(func, wait, scope) {
      var timeout;
      return function() {
        var context = scope || this, args = arguments;
        var later = function() {
          timeout = null;
          func.apply(context, args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
      };
    }

    const test = function (argument) {
      console.log(argument);
    }

    window.addEventListener('resize', debounce(test, 250, this));

Nun gibt die Funktion test() in der Konsole ein Event-Objekt aus. Ich habe schon ein paar Dinge probiert aber ich krieg es nicht hin, meiner Funktion ein Argument meiner Wahl mitzugeben. Ich bin mir sicher, dass das geht. Kann mir jemand auf die Sprünge helfen?

1000 Dank und

beste gruesse, heinetz

  1. Tach!

    Nun gibt die Funktion test() in der Konsole ein Event-Objekt aus.

    Richtig, das ist eines der beiden Dinge, die sie übergeben bekommt. Das andere Ding ist ein Kontext, und der ist entweder das was du als drittes Argument der Funktion debounce() mit auf den Weg gibst oder this zum Zeitpunkt des Aufrufs wenn du einen falsy Wert übergibst. Auf dieses dritte Argument kannst du über this innerhalb von test() zugreifen.

    dedlfix.

    1. Hallo @dedlfix,

      danke, das funktioniert.

      gruss, heinetz

    2. Hallo Forum,

      nun sieht mein Code folgendermassen aus:

      import forEach from '../../../javascripts/dom/forEach';
      
      const imageSlider = document.querySelectorAll('[data-js-module="o-image-slider"]');
      
      const init = async () => {
        const {
          tns
        } = await import('../../../../node_modules/tiny-slider/src/tiny-slider.module');
      
        forEach(imageSlider, (index, element) => {
          
          const debounce = function(func, wait, scope) {
            ...
          };
      
          const render = function(info) {
            console.log('render');
            ...
          };
      
          const slider = tns({
            ...
            onInit: render
          });
      
          window.addEventListener('resize', debounce(render, 250, slider.getInfo()));
          document.addEventListener('DOMContentLoaded', function() {
            console.log('DOM A');
          });
        });
      };
      
      if (imageSlider.length) {
        init();
      }
      
      document.addEventListener('DOMContentLoaded', function() {
        console.log('DOM B');
      });
      

      Das Skript funktioniert nun und ich versuche gerade, es um den Event-Handler 'DOMContentLoaded' zu erweitern. Den habe ich zum Test an zwei Stellen eingebaut aber er wird nur einmal getriggert (Ausgabe ist 'DOM B'). Ich bräuchte ih allerdings eher an der anderen Stelle (Ausgabe wäre 'DOM A'). Warum funktioniert er dort nicht bzw. gibt es eine Möglichkeit, ihn an der Stelle zum Laufen zu bringen?

      danke und gruss, heinetz

      1. Tach!

        Warum funktioniert er dort nicht bzw. gibt es eine Möglichkeit, ihn an der Stelle zum Laufen zu bringen?

        Wenn der Eventhandler nicht aufgerufen wird, dann ist das Event vermutlich bereits gelaufen, bevor du ihn zu registrieren gedenkst. Eine Kontrollausgabe vor dem document.addEventListener(..) von DOM A kann Aufklärung bringen. Wenn sie nach dem "DOM B" erscheint, dann geht es zu dem Zeitpunkt nicht mehr. Du müsstest das Initialisieren in den DOMContentLoaded-Eventhandler legen, der dann so wie der DOM B registriert wird.

        dedlfix.

        1. Hallo @dedlfix,

          ok, so einfach ist das also 😉

          Die Kontrollausgabe betätigt Deine Vermutung. Deinen Lösungsvorschlag sähe dann so aus:

          if (imageSlider.length) {
            document.addEventListener('DOMContentLoaded', function() {
              init();
            });
          }
          

          Das funktioniert grundsätzlich ... bringt aber nicht das erhoffte Ergebnis ;(

          Meine Funktion render() liesst die Höhe eines Containers aus. In diesem Container werden Grafiken geladen ... und sind so massgeblich für dessen Höhe verantwortlich. Ich war davon ausgegangen, dass der Event 'DOMContentLoaded' erst abgefeuert wird, nachdem der Container seine Höhe hat. Offenbar muss ich etwas anderes abfragen ;(

          gruss, heinetz

          1. Tach!

            Meine Funktion render() liesst die Höhe eines Containers aus. In diesem Container werden Grafiken geladen ... und sind so massgeblich für dessen Höhe verantwortlich. Ich war davon ausgegangen, dass der Event 'DOMContentLoaded' erst abgefeuert wird, nachdem der Container seine Höhe hat. Offenbar muss ich etwas anderes abfragen ;(

            DOMContentLoaded a.k.a. DOM-Ready ist, wenn das DOM fertig ist. Da für die eigentliche Anzeige der Seite noch Bilder und andere Ressourcen geladen werden müssen, ist das also noch nicht der Zeitpunkt, zu dem du Größen abfragen kannst. Das ist meines Wissens erst beim Load-Event der Fall.

            dedlfix.

            1. Hallo @dedlfix,

              ich habe mal (nur) den Event 'load' eingebaut. Mit Chrome kein Problem, unter Firefox hat mein Container zu dem Zeitpunkt noch eine Höhe von 0.

              import forEach from '../../../javascripts/dom/forEach';
              
              const imageSlider = document.querySelectorAll('[data-js-module="o-image-slider"]');
              
              const init = async () => {
                const {
                  tns
                } = await import('../../../../node_modules/tiny-slider/src/tiny-slider.module');
              
                forEach(imageSlider, (index, element) => {
                  
                  const debounce = function(func, wait, scope) {
                    ...
                  };
              
                  const render = function(info) {
                    console.log('render');
                    ...
                  };
              
                  const slider = tns({
                    ...
                    //onInit: render
                  });
              
                  //window.addEventListener('resize', debounce(render, 250, slider.getInfo()));
                  window.addEventListener('load', render(slider.getInfo()));
                });
              };
              
              if (imageSlider.length) {
                init();
              }
              

              Da gehen mir langsam die Ideen aus ;( Und Dir?

              gruss, heinetz

              1. Tach!

                ich habe mal (nur) den Event 'load' eingebaut. Mit Chrome kein Problem, unter Firefox hat mein Container zu dem Zeitpunkt noch eine Höhe von 0.

                Dann musst du vielleicht das load-Event von dem Teil nehmen, das geladen sein muss.

                dedlfix.

                1. Das mit dem Load-Event hat funktioniert.

                  Nun habe ich die Funktion ausgelagert und lasse sie von eslint überprüfen. Dort werden mir zwei Fehler ausgegeben, die ich nicht wirklich verstehen und so auch nicht lösen kann.

                  /**
                   * Live event binding on document or different context
                   *
                   * debounceEvent('myFunction', '200', document, 'myArgument');
                   *
                   *
                   * @param   {string} func function to be triggered after timeout
                   * @param   {string} wait duration of the timeuot
                   * @param   {Object} scope arguments for function
                   * @returns {void}
                   */
                  
                  export default (func, wait, scope) => {
                    return function() {
                      const context = scope || this;
                      const args = arguments;
                      let timeout;
                      const later = function() {
                        timeout = null;
                        func.apply(context, args);
                      };
                      clearTimeout(timeout);
                      timeout = setTimeout(later, wait);
                    };
                  };
                  

                  Und so sehen die Fehlermeldungen aus:

                  • 13:39 error Unexpected block statement surrounding arrow body; move the returned value immediately after the => arrow-body-style
                  • 16:18 error Use the rest parameters instead of 'arguments' prefer-rest-params

                  Kannst Du mir etwas dazu sagen?

                  Danke und

                  gruss, heinetz

                  1. Tach!

                    export default (func, wait, scope) => {
                      return function() {
                        const args = arguments;
                      };
                    };
                    

                    Zusammengekürzt auf das wesentliche in Bezug auf die beiden Meldungen.

                    • 13:39 error Unexpected block statement surrounding arrow body; move the returned value immediately after the => arrow-body-style

                    Ich nehme an, dass damit der überflüssige Block mit lediglich dem Return-Statement drin gemeint ist.

                    () => 42 ist eine Schreibweise, bei der eine Funktion definiert wird, die 42 zurückgibt. Solange sich der Funktionskörper als Einzeiler schreiben lässt, braucht man nicht die Langform () => { return 42; }.

                    In deinem Fall geht also auch (...) => function () {...}, denn dabei wird die Funktion auch erzeugt und direkt zurückgegeben. Das lässt sich sogar noch weiter eindampfen, aber ich finde, dann leidet die Lesbarkeit zu stark: (...) => () => {...}

                    • 16:18 error Use the rest parameters instead of 'arguments' prefer-rest-params

                    Rest Parameters ist eine neuere Ergänzung in Javascript. Damit gibt man an, dass beliebig viele Parameter angegeben werden können und dass diese in einem Array abgelegt werden sollen. Der Unterschied zu arguments ist, dass man vorher auch noch andere Parameter angeben kann, die dann aber nicht im Rest-Array auftauchen. Man muss diese festen Parameter also nicht aus arguments rauskürzen, um nur den Rest zu haben.

                    function foo(x, y, ...z) {
                      console.log(x, y);
                    
                      for (let a of z) {
                        console.log(a);
                      }
                    
                      // alternative Verwendung
                      z.forEach(a => console.log(a));
                    }
                    

                    Die drei Punkte ... vor dem z sind in diesem Fall Syntax und kein Auslassungszeichen. Wenn du die Funktion mit foo(1,2,3,4,5) aufrufst, bekommst du neben dem 1 2 für x und y zweimal die 3, 4 und 5 ausgegeben. z ist also ein Array mit den restlichen Parametern, die nicht in x und y gelandet sind.

                    Mit anderen Worten, deine innere Funktion soll so aussehen:

                    return function(...args) {
                        // const args = arguments; kann gelöscht werden
                    };
                    

                    dedlfix.

                    1. hallo @dedlfix,

                      Super. Danke für Deine ausführliche Erklärung!

                      schoenes we, heinetz