AlexioVay
AlexioVay

Reputation: 4527

JavaScript doesn't recognize my function when loading content async?

I want to have a "read more"-click function on a text. I'm dynamically loading a Wikipedia text for a specific keyword and have this function:

function setText(text, lang, full) {
    setTimeout(function () { $('#loading').fadeOut('fast'); }, 300);
    var mylang = '<?= $_SESSION['lang']; ?>';
    if (mylang != 'en' && lang == 'en')
        var AppendText = '<span class="db f09 grey mt10"><?= __('TEXT_ONLY_ENGLISH'); ?></span>';

    console.log(lang);

    if (full) {
        $('#text').html('<?= __('EMPTY'); ?>');
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    } else if (text) {
        // Full Text
        function fullText() {
            console.log('yes');
            setText(text, lang, 1);
        }

        text = text.length > length ? text.substring(0, length - 3) +
        '... <a class="cp" onclick="fullText()"><?= __('MORE') ?></a>' : text;
        $('#text').html(text).append(AppendText);
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    }
};

As you can see in the else if part I defined the fullText() function set to onclick on the anchor tag. I always get the message fullText is not defined when I click on it. I just want to re-call the same setText() function with the variable full set to 1, so the full text will be displayed instead of

Sometext... (more)

Is this possible without using ajax? What options do I have?

PS: I also already tried calling setText() itself instead of fullText() but that won't be recognized as a defined function either.

Upvotes: 0

Views: 88

Answers (3)

Sabumnim
Sabumnim

Reputation: 711

EDIT: The other answers above are better because they take advantage of jQuery and make more sense; my partial solution (only the edits I made) is not dependent on jQuery.

Try something like this:

var fullText;
function setText(text, lang, full) {
    setTimeout(function () { $('#loading').fadeOut('fast'); }, 300);
    var mylang = '<?= $_SESSION['lang']; ?>';
    if (mylang != 'en' && lang == 'en')
        var AppendText = '<span class="db f09 grey mt10"><?= __('TEXT_ONLY_ENGLISH'); ?></span>';

    console.log(lang);

    if (full) {
        $('#text').html('<?= __('EMPTY'); ?>');
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    } else if (text) {
        // Full Text
        fullText = function() {
            console.log('yes');
            setText(text, lang, 1);
        }

        text = text.length > length ? text.substring(0, length - 3) +
        '... <a class="cp" onclick="fullText()"><?= __('MORE') ?></a>' : text;
        $('#text').html(text).append(AppendText);
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    }
};

O_Z is correct in his answer, in that function fullText is only defined inside an instance of setText(). Calling this function from anywhere inside function setText would normally work the way you would expect, however you are calling fullText() from a global onclick handler, by writing the HTML code, and therefore has no clue as to the specific instance of setText() that defined the relevant function fullText.

This solution has one major problem, however, which is that if setText() is called multiple times before the relevant documents are loaded, fullText() would only reference the most recent document, not both. To solve this, I would actually make a global array to store different fullText functions into, and write into the HTML which index in that array to run, and then removing the stored function from the array if it is not necessary anymore (to avoid a memory leak). There is another example below:

var fullTextFunctions = [];
function setText(text, lang, full) {
    setTimeout(function () { $('#loading').fadeOut('fast'); }, 300);
    var mylang = '<?= $_SESSION['lang']; ?>';
    if (mylang != 'en' && lang == 'en')
        var AppendText = '<span class="db f09 grey mt10"><?= __('TEXT_ONLY_ENGLISH'); ?></span>';

    console.log(lang);

    if (full) {
        $('#text').html('<?= __('EMPTY'); ?>');
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    } else if (text) {
        // Full Text
        fullTextFunctions.push(function fullText() {
            console.log('yes');
            setText(text, lang, 1);
        })

        text = text.length > length ? text.substring(0, length - 3) +
        '... <a class="cp" onclick="fullTextFunctions[' + (fullTextFunctions.length - 1) + ']()"><?= __('MORE') ?></a>' : text;
        $('#text').html(text).append(AppendText);
        setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
    }
};

function releaseFullTextFunction(index) {
    fullTextFunctions[index] = null;
}

Upvotes: 1

O_Z
O_Z

Reputation: 1563

Because function fullText() is local to setText the click event executes on the global scope and does not know of fullText() Do not add your A element with inner html but do it by createElement

something like:

var hr = document.createElement("A");
hr.className="cp";
hr.onclick= function  {
            console.log('yes');
            setText(text, lang, 1);
        }
 $('#text').appendChild(hr)

will create a closure and solve the problem.

Upvotes: 1

trincot
trincot

Reputation: 350212

With the onclick attribute you can only reference functions that are in the global scope. To get around this, you can attach an event handler dynamically instead of using the onclick attribute.

To get that done, I would suggest some changes, that lead to the following code:

function setText(text, lang, full) {
    setTimeout(function () { $('#loading').fadeOut('fast'); }, 300);
    var mylang = '<?= $_SESSION['lang']; ?>';
    // change 1: define an element with jQuery 
    var $appendText = $('<span>');
    if (mylang != 'en' && lang == 'en')
        $appendText.addClass('db f09 grey mt10').text('<?= __('TEXT_ONLY_ENGLISH')?>');

    console.log(lang);

    if (full) {
        // change 2: use text(), not html()
        $('#text').text('<?= __('EMPTY'); ?>');
    } else if (text) {
        // Full Text
        // Change 3: define link as jQuery element:
        var $link = $('<a>');
        if (text.length > length) {
            text = text.substring(0, length - 3);
            $link.addClass('cp').text('<?= __('MORE') ?>');
            // Change 4: attach click handler here:
            $link.click(function fullText() {
                console.log('yes');
                setText(text, lang, 1);
            });
        }
        // Change 5: append these jQuery elements, which can be empty:
        $('#text').text(text).append($link).append($appendText);
    }
    // Change 6: move this out of the if-block, as it is the same for both cases:
    setTimeout(function () { $('#text').fadeIn('fast'); }, 500);
};

Note that you did not provide the definition of the variable length, but I assume it is available to the function.

Upvotes: 2

Related Questions