0

I have a static resource JavaScript file that is referenced via <apex:includeScript> that displays a simple typing effect on the VF page however it is not working when previewed and displays as if there is no JS at all.

Do I need to reference the css class differently in VF?

JS

const texts = ['Developer', 'Writer'];

//Counter to iterate through each word
let count = 0;

//Check which word we are iterating over
let index = 0;

//Current word
let currentText = '';

//Letters
let letter = '';

(function type() {
    
    if(count === texts.length) {
        
        count = 0;
    }
    
    //Use count to select text to display
    currentText = texts[count];
    letter = currentText.slice(0, ++index);
    
    document.querySelector('.typing').textContent = letter;
    if(letter.length === currentText.length) {
        count++;
        index = 0;
    }
    
    setTimeout(type, 400);
    
}());

VFP

<apex:page showHeader="false" applyHtmlTag="true" applyBodyTag="false">
<apex:slds >

<head> 
<title>John's Portfolio</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet"/>
    <link href="styles.css" rel="stylesheet"/>
    <apex:stylesheet value="{!URLFOR($Resource.portfolioStyle)}"/>
    <apex:includeScript value="{!URLFOR($Resource.portfolioScript)}"/>
</head>   
<br/>
<div align="center" class="slds-scope">
    <p class="header">John Smith</p>
    <br/>
    
    <div class="row centercontainer">
        <div class="column" align="left">
            <button class="slds-button" style="margin-right: 60px; margin-left: 40px; font-size: 16px">Salesforce</button>
        </div>
        <div class="column" align="center">
            <button class="slds-button" style="margin-right: 60px; margin-left: 40px;font-size: 16px">Software</button>
        </div>
        <div class="column" align="right">
            <button class="slds-button" style="margin-right: 60px; margin-left: 40px;font-size: 16px">About Me</button>
        </div>
    </div>
    
    <br/>
    <div class="container">
    <apex:image url="{!URLFOR($Resource.backgroundimg)}" height="2403" width="903">
    </apex:image>
    <div class="centered">
        <div class="intro">
            <p>A</p> 
            <p class="typing">Salesforce Consultant</p>
        </div>
    </div>
    </div>
    <br/>
    <br/>
    <div> 
</div>
</apex:slds>
</apex:page>

Thanks in advance

1 Answer 1

2

You have a number of problems in your code.

First, in a Visualforce page, don't use applyBodyTag="false" unless you're going to include your own body tag. This causes all kinds of problems otherwise; same goes for omitting the header and HTML tags. There's no need to do this. Just specify your CSS and JS files using apex:stylesheet and apex:includeScript, respectively, they'll end up in the correct place on rendering.

Second, apex:slds is a self-closing tag that should not have any content. Doing it the way you've done it is undocumented and may not work.

Third, your script loads before the page is ready, thus the document.querySelector can't find the element in question--it crashes before it has a chance to run, and therefore never sets the timeout to create the effect you're looking for. Use loadOnReady="true" to let the browser download and parse the DOM before attempting to access it.

Fourth, arguably, you should be using setInterval instead of setTimeout, and call it just once.

Here's all of the corrections applied to the first few lines of your code:

<apex:page title="John's Portfolio">
    <apex:slds />
    <apex:stylesheet value="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
    <apex:stylesheet value="{!URLFOR($Resource.portfolioStyle)}"/>
    <apex:includeScript value="{!URLFOR($Resource.portfolioScript)}" loadOnReady="true" />

I'm not sure what styles.css is supposed to be, but you need to use either a static resource, inline CSS, or an externally hosted CSS (as you did with Bootstrap).

These minimal changes appear to make your typing effect work, but be aware that the word "Salesforce Consultant" gets overwritten and will never be seen if the script loads. You probably want to include that as well, although I'm trying to not make too many assumptions.

Here's the final version of the code (note, I don't have some of your CSS, so it's limited), with some of the extra empty stuff removed for brevity:

<apex:page title="John's Portfolio">
    <apex:slds />
    <apex:stylesheet value="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
    <apex:includeScript loadOnReady="true" value="{!URLFOR($Resource.portfolioScript)}"/>
    <div align="center" class="slds-scope">
        <p class="header">John Smith</p>
        <div class="row centercontainer">
            <div class="column" align="left">
                <button class="slds-button" style="margin-right: 60px; margin-left: 40px; font-size: 16px">Salesforce</button>
            </div>
            <div class="column" align="center">
                <button class="slds-button" style="margin-right: 60px; margin-left: 40px;font-size: 16px">Software</button>
            </div>
            <div class="column" align="right">
                <button class="slds-button" style="margin-right: 60px; margin-left: 40px;font-size: 16px">About Me</button>
            </div>
        </div>
        
        <div class="container">
            <div class="centered">
                <div class="intro">
                    <p>A Salesforce</p> 
                    <p class="typing">Consultant</p>
                </div>
            </div>
        </div>
    </div>
</apex:page>

And your script (which honestly needs some more work, too):

const texts = ['Consultant', 'Developer', 'Writer'];

//Counter to iterate through each word
let count = 0;

//Check which word we are iterating over
let index = 0;

//Current word
let currentText = '';

//Letters
let letter = '';

(function type() {
    
    if(count === texts.length) {
        
        count = 0;
    }
    
    //Use count to select text to display
    currentText = texts[count];
    letter = currentText.slice(0, ++index);
    
    document.querySelector('.typing').textContent = letter;
    if(letter.length === currentText.length) {
        count++;
        index = 0;
    }
    
    setTimeout(type, 400);
    
}());

And finally, a demonstration of your page in action:

Animation of final result

1
  • Thank you sfdcfox, this helped/worked. Happy new year
    – SpicyPat
    Commented Jan 3, 2021 at 17:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .