How to move javascript scripts out of php files to *.js files?

How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Number of replies: 21
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

I am almost done with upgrading my REGEXP question type from Moodle 1.9 through 2.0 to 2.1. I have a couple of very short javacript bits of code in my rendered.php file, which work fine, but I undertand that current moodle good practice requires all such scripts to be removed and placed in their own *.js files.

However, I haven't a clue how to achieve this.

Here is one example, which is uses a button to alternatively display/hide a list of acceptable answers to a REGEXP question, for the teacher in Preview mode.

        //js script for showing / hiding regexp generated alternative sentences (for teacher only)//
        if ($ispreview) {
            $response = $question->get_correct_response();
            $correctanswer = $response['answer'];      
            $show = get_string("showalternate", "qtype_regexp");
            $hide = get_string("hidealternate", "qtype_regexp");
            echo("<input type=\"button\" value=\"$show\" onclick=\"showdiv('allanswers',this)\" />");
            echo ("<script>
            function showdiv(id,thisbutton) {
                if (document.getElementById(id).style.display == 'none') {
                    document.getElementById(id).style.display = 'block'
                    thisbutton.value = '".$hide."'
                } else {
                    document.getElementById(id).style.display = 'none'
                    thisbutton.value = '".$show."'
                }
                return;
            }
            </script>");
        }

Help on how to move this away to a script.js file would be appreciated.

Joseph

Average of ratings: -
In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
I think you mean module.js file!

Have you read all this stuff... http://docs.moodle.org/dev/JavaScript_guidelines ??

And this... http://docs.moodle.org/dev/Javascript_and_YUI_3_FAQ

I found it incredibly confusing last time I had to do something like this. I'm reasonably confident that I'll be confused all over again next time.
In reply to Howard Miller

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Hi Howard,

Thanks for pointing me to those resources and ... sharing your confusion.big grin

ATB

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Note, your code won't work if there are two questions on the page, since the id 'allanswers' woud be the same in both questions, and my adaption below suffers from the same issue.

The following is un-tested, but about right:

 

In question/type/regex/module.js

M.qtype_regex = M.qtype_regex || {};

M.qtype_regex.init = function(Y, buttonel, showhideel) {
Y.on('click', function(e) {
if (Y.one(showhideel).getStyle('display') == 'none') {
    Y.one(showhideel).show();
    Y.one(buttonel).set('value', M.util.get_string('hidealternate', 'qtype_regexp'));
} else {
    Y.one(showhideel).hide();
    Y.one(buttonel).set('value', M.util.get_string('hidealternate', 'qtype_regexp'));
}
e.halt();
}, element);
}

in renderer.php

$id = something unique;
$output .= html_writer::empty_tag('input', array('type' => 'button', 'value' => get_string("showalternate", "qtype_regexp"), 'id' =>$id));
$this->page->requires->js_init_call('M.qtype_multianswer.init', array('#' . $id, '#allanswers'), false, array(
'name' => 'qtype_multianswer',
'fullpath' => '/question/type/multianswer/module.js',
'requires' => array('base', 'node', 'event'),
'strings' => array(array('showalternate', 'qtype_regexp'), array('hidealternate', 'qtype_regexp')),
));

 

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Hi Tim and many thanks for your contribution.

1.- There is no display problem with this bit of code, because it is only used on the Preview page, where precisely only one question is displayed at a time.

2.- I have had to slightly modify the JS code you provided, as I could not get the Y.one(showhideel).show(); and Y.one(showhideel).hide(); bits to work. Here is what I finally did, which does work fine:

M.qtype_regexp = M.qtype_regexp || {};

M.qtype_regexp.init = function(Y, buttonel, showhideel) {
    Y.on('click', function(e) {
        if (Y.one(showhideel).getStyle('display') == 'none') {    
            Y.one(showhideel).setStyle('display', 'block');
            Y.one(buttonel).set('value', M.util.get_string('hidealternate', 'qtype_regexp'));
        } else {
            Y.one(showhideel).setStyle('display', 'none');
            Y.one(buttonel).set('value', M.util.get_string('showalternate', 'qtype_regexp'));
        }
        e.halt();
    });
}

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

After further checking, actually:

Y.on('click', function(e) {

should rather be:

Y.one(buttonel).on('click', function(e) {

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Oops, there was a typo in my code:

Y.on('click', ... function ..., buttonel);

is exactly equivalent to

Y.one(buttonel).on('click', ... function ...);

However, I missed out the , buttonel bit in my code.

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

1.- Another typo? the following works:

Y.on('click', function(e, buttonel) { ... } )

2.- I have yet another problem in transforming good old javascript scripts into Yahoo java style. I need to retrieve the value of an input field (the student's answer field) Let's say that field's name and id is "q549:1_answer", which is passed on in the inputname parameter.

a) the following works:

actualvalue = document.getElementsByName(inputname)[0].value;

b) I try to translate this into yahoo style:

actualvalue = Y.one(inputname).get('value');

which triggers error: Y.one(inputname) is null.

What am I doing wrong (again)?

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

document.getElementById('id')

Y.one('#id')

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Hi Tim,

Yes, I get it that Y.one needs an extra '#' character prefix, but... that's not the problem at hand.

The following works fine:

inputname = "q549:1_answer"

actualvalue = document.getElementsByName(inputname)[0].value;

 

The following do not work:

inputname = "q549:1_answer"

actualvalue = document.getElementById(inputname)[0].value;

inputname = "#q549:1_answer"

actualvalue = Y.one(inputname).get('value');

Why is that?

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Bernard Boucher -

Bonjour Joseph,

                            I am not totally sure, but it think that document.getElementById(inputname)[0].value

return only one element ( the first one ) and you can't access it like an array with your [0].

getElementsByName, with the plural of elements, return all elements in an array.

I did'nt test it but  document.getElementById(inputname).value  should work.

I hope I have understand clearly your problem.

 

Salutations from Québec,

 

Bernard

 

In reply to Bernard Boucher

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Merci Bernard !

You are right, of course, blush

document.getElementById(inputname).value

does work.

However, I still have a problem with

inputname = "#q549:1_answer"

actualvalue = Y.one(inputname).get('value');

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

name != id

Does the input have an id attribute?

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Well, in the case at hand, name = id:

$inputattributes = array(
            'type' => 'text',
            'name' => $inputname,
            'value' => $currentanswer,
            'id' => $inputname,
            'size' => 80,
        );

If the input did not have an id,

document.getElementById(inputname).value

would not work, would it?

And when I try to pass that $inputname value to the js file, I make sure to prefix it with '#':

$this->page->requires->js_init_call('M.qtype_regexp.showhint', array('#' .$id, '#' .$inputname, $hintflag), false, array(...)

Here is an extract from my js contents:

M.qtype_regexp.showhint = function(Y, buttonel, inputname, hintflag) {
    Y.one(buttonel).on('click', function(e) {
        alert (inputname);
        actualvalue = Y.one(inputname).get('value');
        ...
   });
}

Gives:

alert: #q635:1_answer

error: Y.one(inputname) is null

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Upon further investigation of the problem, I have come to the following conclusion.

On the moodle/question/preview.php and moodle/mod/quiz/attempt.php pages, there are 3 "elements" pertaining to the question which are "outside of the scope" of the YUI "domain" and cannot be accessed by using Y.oneegg. Whereas all other elements on the page can.

For instance, in the case of a question labelled q648:

<input type="hidden" name="q648:1_:sequencecheck" value="1" />
<input type="text" name="q648:1_answer" id="q648:1_answer" size="80" />
<input type="submit" id="q648:1_-submit" name="q648:1_-submit" value="Check" class="submit btn" />

None of the above input elements can be accessed through the YUI system. Note: The only one I need to access is the one in blue.

It's not so bad (for my purposes) because I can always use the good old DOM commands such as document.getElementByIdegg. But is is puzzling.thoughtful

Joseph

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Yes, it is puzzling. It definitely should work.

In reply to Joseph Rézeau

Re: How to move javascript scripts out of php files to *.js files?

by Urs Hunkler -
Picture of Core developers
Joseph,

your element id is not valid XHTML and therefore YUI refuses to find it I suppose.

Valid characters for IDs and CLASSes:

The id attribute value must begin with a letter in the roman alphabet (a–z or A–Z); this can be followed by any combination of letters (a–z or A–Z), digits (0–9), hyphens "-", underscores "_", colons ":", and periods ".". (From http://reference.sitepoint.com/html/core-attributes/id)

you may change your ID and the ID finding will work in YUI.

By the way it's strange that document.getElementById finds the element - may be because the browser is prepared for HTML5 where more characters are allowed.

Edit: I reread the list of allowed characters more carefully and noticed that the colon is allowed - I didn't know that. So your ID should be valid and YUI must find the element. Otherwise it's a YUI bug.

Have you tried using different names for name and id?
In reply to Urs Hunkler

Re: How to move javascript scripts out of php files to *.js files?

by Urs Hunkler -
Picture of Core developers
YUI has a bug here.

Have a look at http://jsbin.com/ayesay/8/edit#source to see that an ID without ":_" works in YUI.

jQuery works - another reason to switch to jQuery I think.
In reply to Urs Hunkler

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Urs,

Thanks for clarifying this "mystery"!

@Tim,

Would you consider modifying the get_field_prefixegg function in questionattempt.php so that the question field name and id are more YUI compliant?

Joseph

In reply to Urs Hunkler

Re: How to move javascript scripts out of php files to *.js files?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Acutally, no. The only bug is with the W3C web standards. Or at least a very annoying 'feature'.

As you say, : is a valid character in an id. (http://dev.w3.org/html5/spec/Overview.html#concept-id, http://www.w3.org/TR/2006/REC-xml11-20060816/#NT-Name)

However, in CSS selectors (http://www.w3.org/TR/CSS21/syndata.html#tokenization), : is not allowed in the {name} bit of #{name}. I think \072 can be used in place of :. This is because something like a#linkid:active is a valid CSS selector for an active link. That is : is significant in CSS gramar.

Functions like YUI.one() and $() are basically wrappers around the standardised, but not widely implemented document.querySelector() (http://www.w3.org/TR/selectors-api/#nodeselector). They take a CSS selector as an argument.

Therefore Y.one('#q549:1_answer'); looks for an element with id q549, and pseudo-selector 1_answer - which is invalid. Y.one('#q549\0721_answer'); might work.

From what you are saying, jQuery seems to automatically escape the : in this case, which is a really nice feature of jQuery.

In terms of what to do in the question engine, for a long time I was using , instead of :, which was very natural. Unfortunately that is not allowed in XHMTL, and #q549,1_answer is not a valid CSS selector either. Well, it is, it matches an element that either has id q549, or an <1_answer> element.

If you look at the other characters that are allowed in ids in XHTML, then I there is really not much else. I am already using - and _ for other purposes. Which is why I ended up using : here. You can always do Y.one(document.getElementById(id)). That works.

But I wish someone at W3C had noticed this inconsistency early enough to avoid it ocurring in the first place. Still, it is too late now.

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Thanks for the detailed explanation, Tim.

No problem as far as I'm concerned, this is not a blocker.

ATB

Joseph

In reply to Tim Hunt

Re: How to move javascript scripts out of php files to *.js files?

by Urs Hunkler -
Picture of Core developers
@Acutally, no. The only bug is with the W3C web standards. Or at least a very annoying 'feature'.

We talk here about a JavaScript library which should make working with JavaScript and browsers easier. The basic getElementByID() gets ":", jQuery gets ":" in ID {name}. YUI fails. This I call a bug because it does not work as expected.

From the JavaScript point of view it can't play a role how a library finds elements. I just expect no differences between basic JavaScript and a library.

Differences lead to unnecessary frustrating experiences as we see.