> ## Documentation Index
> Fetch the complete documentation index at: https://docs.redem.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Behavior Tracking

> Learn how to track respondent behavior

<Note>
  ReDem only uses the last 300 `MOUSE_MOVEMENT` and last 300 `MOUSE_CLICK` events per BAS data point. Cap client-side tracking at the same limits — storing more only inflates browser session data without improving scoring.
</Note>

<Steps titleSize="h3">
  <Step title="Implementing a JavaScript Tracker for User Behavior Data">
    <p> Copy the simple **JavaScript tracker code** (attached) into your survey platform’s frontend codebase or UI. This ensures that, whenever respondent complete surveys, it tracks input behavior data for open-ended questions directly from their individual browsers. </p>
  </Step>

  <Step title="Integrate ReDem API into your workflow">
    <p>We are using this script to persist this input\_behavior in the browser until you are ready to call the API</p>

    ```javascript theme={null}
    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const textarea = document.getElementById("input_behavior");
        const submitButton = document.getElementById("submit_button");
      
        // Constants
        const INTERACTION_TYPES = {
            KEYSTROKE: 'KEYSTROKE',
            COPYPASTE: 'COPY_AND_PASTE',
            MOUSE_MOVEMENT: "MOUSE_MOVEMENT",
            MOUSE_CLICK: "MOUSE_CLICK",
        };
      
        // Question ID - This would typically come from your survey tool, you need to edit this
        const QUESTION_ID = "<BAS_QuestionID>";
        const MAX_MOUSE_MOVEMENT_EVENTS = 300;
        const MAX_MOUSE_CLICK_EVENTS = 300;
        const INTERACTION_STORAGE_KEY = QUESTION_ID + "_interaction_data";

        const movementEvents = [];
        const clickEvents = [];
        const keystrokeEvents = [];

        const createInteractionRecord = (value, interactionType) => ({
            value,
            interactionType,
            timestamp: new Date().toISOString()
        });

        const applyCap = (buffer, maxEvents) => {
            while (buffer.length > maxEvents) {
                buffer.shift();
            }
        };

        const pushToCappedBuffer = (buffer, record, maxEvents) => {
            buffer.push(record);
            applyCap(buffer, maxEvents);
        };

        const splitIntoInteractionBuffers = (interactionData) => {
            interactionData.forEach((event) => {
                if (event.interactionType === INTERACTION_TYPES.MOUSE_MOVEMENT) {
                    movementEvents.push(event);
                    return;
                }
                if (event.interactionType === INTERACTION_TYPES.MOUSE_CLICK) {
                    clickEvents.push(event);
                    return;
                }
                keystrokeEvents.push(event);
            });
            applyCap(movementEvents, MAX_MOUSE_MOVEMENT_EVENTS);
            applyCap(clickEvents, MAX_MOUSE_CLICK_EVENTS);
        };

        const loadInteractionBuffers = () => {
            const stored = sessionStorage.getItem(INTERACTION_STORAGE_KEY);
            if (!stored) {
                return;
            }
            splitIntoInteractionBuffers(JSON.parse(stored));
        };

        const mergeInteractionDataChronologically = () =>
        [...movementEvents, ...clickEvents, ...keystrokeEvents].sort((a, b) =>
            a.timestamp.localeCompare(b.timestamp)
        );

        const saveInteractionDataToSession = () => {
            sessionStorage.setItem(
                INTERACTION_STORAGE_KEY,
                JSON.stringify(mergeInteractionDataChronologically())
            );
        };

        loadInteractionBuffers();

        // Handle mouse movement events
        document.addEventListener("pointermove", (e) => {
            pushToCappedBuffer(
                movementEvents,
                createInteractionRecord(
                    { x: e.clientX, y: e.clientY },
                    INTERACTION_TYPES.MOUSE_MOVEMENT
                ),
                MAX_MOUSE_MOVEMENT_EVENTS
            );
        });

        // Handle mouse click events
        document.addEventListener("click", (e) => {
            const target = e.target instanceof Element ? e.target : null;
            if (!target) return;

            const rect = target.getBoundingClientRect();
            pushToCappedBuffer(
                clickEvents,
                createInteractionRecord(
                    { width: rect.width, height: rect.height, offsetX: e.clientX - rect.left, offsetY: e.clientY - rect.top },
                    INTERACTION_TYPES.MOUSE_CLICK
                ),
                MAX_MOUSE_CLICK_EVENTS
            );
        });
        
      
        // Handle Keystroke events
        textarea.addEventListener("input", (e) => {
            // Ignore paste events as they're handled separately
            if (e.inputType === "insertFromPaste") return;
          
            keystrokeEvents.push(createInteractionRecord(e.target.value, INTERACTION_TYPES.KEYSTROKE));
        });
      
        // Handle paste events
        textarea.addEventListener("paste", (e) => {
            const selectionStart = e.target.selectionStart;
            const selectionEnd = e.target.selectionEnd;
            const currentValue = e.target.value;
            const pastedText = e.clipboardData?.getData("text") || "";
            const newText = currentValue.slice(0, selectionStart) + pastedText + currentValue.slice(selectionEnd);

            keystrokeEvents.push(createInteractionRecord(newText, INTERACTION_TYPES.COPYPASTE));
        });
      
        // Handle form submission
        submitButton.addEventListener("click", function () {
            saveBASDataPerQuestion();
        });

        window.addEventListener("pagehide", saveInteractionDataToSession);


        // Function to save data in BAS format
        const saveBASDataPerQuestion = () => {
            const interactionData = mergeInteractionDataChronologically();
            const basData = {
                "qualityCheck": "BAS",
                "dataPointId": QUESTION_ID,
                "interactionData": interactionData
            };
          
            sessionStorage.setItem(QUESTION_ID, JSON.stringify(basData));
            saveInteractionDataToSession();
        };
      });
    </script>
    ```
  </Step>

  <Step title="Use API response to trigger your next step">
    <p>When you are making the API call to ReDem use this structure from the session storage and make sure to connect `QUESTION_ID` during the integration process</p>
  </Step>
</Steps>
