最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React: target dynamic HTML elements with Intro.js - Stack Overflow

programmeradmin0浏览0评论

I have been using Intro.js for React for a while now, and reallt like it. However, I am not able to fix the one issue that is keeping me from continuing with the tours. That is the fact that I cannot seem to be able to target dynamic HTML elements that are either refreshed or loaded after a certain state change. I discovered Intro.js has a static array of HTML target elements on boot, but does not update this state when something changes. Is there any solution to change this to allow for resetting Intro.js during the tour for example? So the array of targetted HTML elements is refreshed.

I am using the following setup:

let steps: {
    intro: string,
    element?: string
}[] = [];

/* Construct Intro.js steps for digital specimen page */
digitalSpecimenSteps.forEach((step, index) => {
    if ([0, 1, 2, 3, 4, 5].includes(index) || (KeycloakService.IsLoggedIn() && KeycloakService.GetParsedToken()?.orcid)) {
        steps.push({
            intro: step,
            element: `.tourAnnotate${index + 1}`
        });
    }
});

<Steps enabled={tourTopic === 'annotate'}
    steps={steps}
    initialStep={0}
    onBeforeChange={(nextIndex) => {
        return new Promise((resolve) => {
            OnStepChange(nextIndex + 1, resolve);
        });
    }}
    onStart={() => {
        dispatch(setAnnotationWizardToggle(false));
    }}
    onExit={() => {
        dispatch(setTourTopic(undefined));
        dispatch(setAnnotationWizardToggle(false));
    }}
    options={options}
/>

/**
 * Function that checks what to do on a step change
 * @param nextIndex The next (selected) index in the step chain
 * @param resolve Function to resolve the step promise 
 */
const OnStepChange = async (nextIndex: number, resolve: Function) => {
    if ([1, 2, 3].includes(nextIndex) && annotationMode) {
        SetAnnotationMode(false);

        await new Promise((resolve) => {
            setTimeout(() => {
                resolve(true);
            }, 500);
        });
    } else if (![1, 2, 3].includes(nextIndex) && !annotationMode) {
        SetAnnotationMode(true);

        await new Promise((resolve) => {
            setTimeout(() => {
                resolve(true);
            }, 500);
        });
    }

    if (nextIndex < 7 && tourAnnotationWizardToggle) {
        dispatch(setAnnotationWizardSelectedIndex(undefined));
        dispatch(setAnnotationWizardToggle(false));

        setTimeout(() => {
            resolve();
        }, 500);
    } else if (nextIndex === 7) {
        dispatch(setAnnotationWizardToggle(true));

        resolve();
    } else {
        resolve();
    }
}

The function OnStepChange function changes some state variables in the global state, determining if certain HTML elements should be rendered or not. Step 6 targets a dynamic element, it is visible at first and registered by Intro.js, so the tour acts normal. At step 7, the element gets removed from the DOM, returning to step 6, it is rendered again. In this return step, Intro.js loses its focus to the element and does not target it with the tooltip lay-over.

Any clues on how I can make Intro.js recognize the dynamic HTML element that was removed, but later again added to the DOM?

Thanks!

Solution

The solution was to implement a function that updates the tour step when it is reached. Apparently there is a function for this in intro.js called updateStepElement, in which you provide the index of the step to update. To access this method I put a ref on the Steps component and accessed it in the onBeforeChange event:

setTimeout(() => {
    stepsRef.current.updateStepElement(nextIndex);
resolve(true); }, 500);

Provided a time out to give the DOM the proper time to update, otherwise it will still miss the mark.

发布评论

评论列表(0)

  1. 暂无评论