Elxis CMS Forum

Support => Technical support => Topic started by: apkoutsou on August 01, 2010, 10:34:53

Title: Call javascript functions
Post by: apkoutsou on August 01, 2010, 10:34:53
I'm a bit stuck here: I want to call a function declared inside the <head> section of my .html file from an external .js file included also inside the <head> section, but I get an undeclared error message (which is quite logical as the .html function is not inside .js file scope). Is there a way to overcome this one?

index.html:

Code: [Select]
<html>
    <head>
        <script type="text/javascript" src="includes/controls/controls.js"></script>
       
        <script type="javascript/text">
            function headFunction() {
                alert('Hello World!');
            }
        </script>   
    </head>

    <body bgcolor="#FFFFFF" onload="load_function();">
   
    </body>
</html>

controls.js:

Code: [Select]
function load_function() {
    headFunction();
}
Title: Re: Call javascript functions
Post by: datahell on August 01, 2010, 10:54:50
Why dont you initialize the function in the head section of the page?

function headFunction() {
      alert('Hello World!');
}
headFunction();
Title: Re: Call javascript functions
Post by: apkoutsou on August 01, 2010, 14:49:36
What I'm trying to do is this:

The load_function() will search if a function is declared and then connect it as an event to the proper element, so it has to be able to see all function (in any .js file or inside the .html file).
E.g., if i have the element

Code: [Select]
<input type="button" id="buttonAccept"/>
and a function

Code: [Select]
function buttonAccept_onClick() { ... }
then the load_function() will connent them with the onclick event

Code: [Select]
document.getElementById('buttonAccept').onclick = function() { buttonAccept_onClick() }
The load_function is a general function, so it will be inside the controls.js file, but the buttonAccept_onClick() may be inside the .html file or any other .js file.

I'll propably use mooTools, but I am curious how could this in simple Javascript be done...
Title: Re: Call javascript functions
Post by: datahell on August 01, 2010, 15:03:44
Search for any function that is declared? Including all functions in html, external files and libraries, js built-in functions, etc? This does nt look a good idea to me at all. Also under some occasions you will surely have conflicts and wrong loadings due to common names. For instance if I create an element with id="alert" you will trigger the alert() function for that element?

Why dont you do what you need in a different way? For instance lightbox-style scripts use the "rel" attribute inside links to set which links will be triggered. I am sure that you can do what you want in a much simpler and faster way but I can not propose you something as I dont know why you try to do that.

Simple javascript can do anything you wish, mootools and the other libraries are just shortcuts.
Title: Re: Call javascript functions
Post by: apkoutsou on August 01, 2010, 19:31:29
The reason I'm doing this is to make my coding style much more cleaner and faster - and more OO: When I create a form, all I have to do is just write the <input> tag and set its attributes - any event relative to the input will be a javascript function named after the input name followed by an underscore and the event type (onClick, onBlur etc). If I want to add an event all that has to be done is add a similar function; if I want to remove an event I'll just remove that function - without having also to search for the input and add/remove there the events.

Here is a very simple example:
Code: [Select]
<html>
    <head>
        <script type="text/javascript" src="includes/controls/controls.js"></script>
       
        <script type="text/javascript">
            function buttonAccept_onClick() { document.getElementById("frmMain").submit; }

            function buttonClear_onClick() { document.getElementById("txtName").value = "Enter your name" }

            function txtName_onBlur() { if (this.value == "") this.value = "Enter your name"; }

            function txtName_onFocus() { if (this.value == "Enter your name") this.value=""; }
        </script>   
    </head>

    <body bgcolor="#FFFFFF" onload="init_events();">
      <form id="frmMain" name="frmMain method="post" action="register.php">
         <input type="text" id="txtName" name="txtName" value="Enter your name" />
         <input type="button" id="buttonAccept" name="buttonAccept" value="Accept" />
         <input type="button" id="buttonClear" name="buttonClear" value="Clear" />
      </form>
    </body>
</html>

The init_events() function will find all input elements and then check if a function named after the name/id of the input element followed by an event type is declared and if so, it will connect the input element with that function as that event (type). The init_events() function can be inside the .html file header (causing no trouble at all), but as a general function I want to place it inside the includes/controls/controls.js file among with other general functions related to element events...
Title: Re: Call javascript functions
Post by: datahell on August 01, 2010, 21:57:36
I tried to re-create it the way you want it but it does not work correctly. The best I managed to do is to trigger the first event. I believe that there are much easier ways and problems-free to do what you want. Dont get stacked on that option.

Here is a part of my tests.

Code: [Select]
function getallids() {
var allTags = document.body.getElementsByTagName('*');
var ids = [];
for (var tg = 0; tg< allTags.length; tg++) {
    var tag = allTags[tg];
    if (tag.id) { ids.push(tag.id); }
    }
    return ids;
}

function isFunction(funcname){
    var isDefined = eval('(typeof '+funcname+'==\'function\');');
    if (isDefined) { return true; } else { return false; }
}

function init_events() {
var myEvents = new Array("click","blur","focus");
var allIds = getallids();
var myfunc = null;
var curid = '';
var curevent = '';
for (var x in allIds) {
for (var i in myEvents) {
curid = allIds[x];
curevent = myEvents[i];
myfunc = curid+'_on'+curevent;
if(isFunction(myfunc)) {
document.getElementById(curid).addEventListener(curevent,function() {
myfunc = eval(curid+'_on'+curevent);
myfunc();
}, false);
}
}
}
}
Title: Re: Call javascript functions
Post by: apkoutsou on August 02, 2010, 00:09:47
I did it!!

Here is the code:

Code: [Select]
function init_events() {
var myEvents = new Array("Click","Blur","Focus");
var allIds = getallids();
var myfunc = null;
var curid = '';
var curevent = '';
for (var x in allIds) {
for (var i in myEvents) {
curid = allIds[x];
curevent = myEvents[i];
myfunc = curid+'_on'+curevent;
if(isFunction(myfunc)) {
if (document.getElementById(curid).addEventListener){
document.getElementById(curid).addEventListener(curevent.toLowerCase(),function(e) {
eType = e.type;
switch (eType) {
case 'click': eType = 'Click'; break;
case 'focus': eType = 'Focus'; break;
case 'blur': eType = 'Blur'; break;
}
myfunc = this.id + '_on' + eType;
if(isFunction(myfunc)) {
myfunc = eval(this.id + '_on' + eType);
myfunc();
}
}, false);
} else if (document.getElementById(curid).attachEvent){
var r = document.getElementById(curid).attachEvent("on"+curevent.toLowerCase(), function(e) {
myfunc = eval(curid+'_on'+curevent);
myfunc();
});
}
}
}
}
}

The rest are ok. The problem was that the anonymus function() -that was connected to the event- had the curid and curevent variables, that stayed with their last values (after the for loops), so the same function was triggered for every event.

It is a bit complicated - It may be an easier way to achive it, so I'll see can I can do...

Thanx datahell for your help!

P.S.: The way I see it, I believe that the anonymus function() should be connected to all events of all elements; this function() will check every time if the right function exists depending on the element and event...
Title: Re: Call javascript functions
Post by: datahell on August 02, 2010, 19:30:01
I am happy that you solved it, it was quite difficult.
But I insist that you should find a simpler way to attach events on the elements you wish to.
Title: Re: Call javascript functions
Post by: apkoutsou on August 02, 2010, 22:30:05
The whole point is, datahell, to find a way to automate the creation of events. The real goal is to write only the event function in a way that the program will understand itself that this function should be called when an event is triggered! That's why this function naming convention; <element name>_on<event type>!

I'm trying to find a way (using javascript and php) to make form creation seem like a game. Here is my testing progress:

includes/controls/controls.css
Code: [Select]
.textbox {
color: #333333;
background-color: #ffffff;
background-image: none;
background-repeat: repeat;
background-attachment: scroll;
background-position: 0% 0%;
z-index: -3;
font-family: tahoma,verdana,arial;
font-size: 11px;
padding-top: 2px;
padding-right: 2px;
padding-bottom: 2px;
padding-left: 2px;
}
.textbox-disabled {
color: #AAAAAA;
background-color: #ffffff;
background-image: none;
background-repeat: repeat;
background-attachment: scroll;
background-position: 0% 0%;
z-index: -3;
font-family: tahoma,verdana,arial;
font-size: 11px;
padding-top: 2px;
padding-right: 2px;
padding-bottom: 2px;
padding-left: 2px;
}

.button {
color: #333333;
background-color: #AAAAAA;
background-image: none;
background-repeat: repeat;
background-attachment: scroll;
background-position: 0% 0%;
z-index: -3;
font-family: tahoma,verdana,arial;
font-size: 11px;
padding-top: 2px;
padding-right: 2px;
padding-bottom: 2px;
padding-left: 2px;
}
.button-disabled {
color: #666666;
background-color: #DDDDDD;
background-image: none;
background-repeat: repeat;
background-attachment: scroll;
background-position: 0% 0%;
z-index: -3;
font-family: tahoma,verdana,arial;
font-size: 11px;
padding-top: 2px;
padding-right: 2px;
padding-bottom: 2px;
padding-left: 2px;
}
includes/controls/controls.js
Code: [Select]
function getInputIds() {
var arrInputTags = document.body.getElementsByTagName("input");
var arrReturnIds = [];
for (var iTag = 0; iTag < arrInputTags.length; iTag++) {
    var tagInput = arrInputTags[iTag];
    if (tagInput.id) { arrReturnIds.push(tagInput.id); }
    }
    return arrReturnIds;
}

function isFunction(funcname){
    var bDefined = eval( '(typeof ' + funcname + '==\'function\');' );
    if (bDefined) { return true; } else { return false; }
}

function initEventHandler() {
var arrEvents = new Array("click", "blur", "focus");
var arrInputIds = getInputIds();
var funcEventHandler = null;
var strCurId = '';
var strCurEvent = '';
for (var x in arrInputIds) {
for (var i in arrEvents) {
strCurId = arrInputIds[x];
strCurEvent = arrEvents[i];
if (document.getElementById(strCurId).addEventListener){
document.getElementById(strCurId).addEventListener(strCurEvent, function(e) {
evType = e.type;
switch (evType) {
case 'click': evType = 'Click'; break;
case 'focus': evType = 'Focus'; break;
case 'blur': evType = 'Blur'; break;
}
funcEventHandler = this.id + '_on' + evType;
if ( isFunction(funcEventHandler) ) {
funcEventHandler = eval(this.id + '_on' + evType);
funcEventHandler();
}
}, false);
} else if ( document.getElementById(strCurId).attachEvent ) {
var r = document.getElementById(strCurId).attachEvent("on" + strCurEvent, function(e) {
evType = e.type;
switch (evType) {
case 'click': evType = 'Click'; break;
case 'focus': evType = 'Focus'; break;
case 'blur': evType = 'Blur'; break;
}
funcEventHandler = this.id + '_on' + evType;
if ( isFunction(funcEventHandler) ) {
funcEventHandler = eval(this.id + '_on' + evType);
funcEventHandler();
}
});
}
}
}
}
includes/controls/controls.php
Code: [Select]
<?php

$arrControls 
= Array();

class 
clsHTMLControls {

public function startForm$strName$strMethod "post"$strAction NULL$strStyle NULL ) {
global $arrControls;

echo "<form id= \"".$strName."\" name=\"".$strName."\" ";
echo "method=\"".( (in_array($strMethod, array("post""get")))?$strMethod:"post" ). "\" ";
echo "action=\"".$strAction."\" ";
if ($strStyle) {
echo "style=\"".$strStyle."\" ";
}
echo "> \n";

$arrControls[] = $strName;
}


public function addTextBox$strName$strDefaultValue NULL$strSize NULL$strMaxLangth NULL$bReadOnly False$bDisabled False$strStyle NULL ) {
global $arrControls;

echo "<input type=\"text\" ";
echo "id= \"".$strName."\" name=\"".$strName."\" ";
echo "value=\"".$strDefaultValue."\" ";
if ($strSize) { echo "size=\".".$strSize.".\" "; }
if ($strMaxLangth) { echo "maxlength=\"".$strMaxLangth."\" "; }
if ($bReadOnly) { echo "readonly=&#39;readonly&#39; "; }
if ($bDisabled) { echo "disabled=\"".strval($bDisabled)."\" "; }
echo "class=\"textbox".( ($bDisabled)?"-disabled":"" )."\" ";
if ($strStyle) {
echo "style=\"".$strStyle."\" ";
}
echo "/> \n";

$arrControls[] = $strName;
}

public function addButton $strName$strValue NULL$bDisabled False$strStyle NULL ) {
global $arrControls;

echo "<input type=\"button\" ";
echo "id= \"".$strName."\" name=\"".$strName."\" ";
echo "value=\"".( ($strValue)?$strValue:"Command Button" )."\" ";
if ($bDisabled) { echo "disabled=\"".strval($bDisabled)."\" "; }
echo "class=\"button".( ($bDisabled)?"-disabled":"" )."\" ";
if ($strStyle) {
echo "style=\"".$strStyle."\" ";
}
echo "/> \n";

$arrControls[] = $strName;

}

public function initControls() {
global $arrControls;

echo "<script type=\"type/javascript\" > \n";
foreach ($arrControls as $objControl) {
echo "var ".$objControl." = document.getElementById(\"".$objControl."\"); \n";
}
echo "</script> \n";
}

public function endForm() {
echo "</form> \n";
}

}
?>
index.php
Code: [Select]
<?php require_once("includes/controls/controls.php"); ?>

<html>
<head>
<link href="includes/controls/controls.css" rel="stylesheet" type="text/css" media="all" />
<script type="text/javascript" src="includes/controls/controls.js"></script>

<script type="text/javascript">
<!--
           
// ------------- cmdAccept events ------------- //
function cmdAccept_onClick() {
if ( txtName.value == "Enter your name...") {
alert("Please enter your name!");
} else {
frmMain.submit();
}
}

// ------------- cmdClear events ------------- //
function cmdClear_onClick() {
txtName.style.fontStyle = "italic";
txtName.style.color = "#999999";
txtName.value = "Enter your name...";
}

// ------------- txtName events -------------- //
            function txtName_onBlur() {
if (txtName.value == "") {
txtName.style.fontStyle = "italic";
txtName.style.color = "#999999";
txtName.value = "Enter your name...";
}
}

            function txtName_onFocus() {
if (txtName.value == "Enter your name...") {
txtName.style.fontStyle = "normal";
txtName.style.color = "#333333";
txtName.value = "";
}
}

// -->
</script>

</head>

<body bgcolor="#FFFFFF" onload="initEventHandler();">

<?php clsHTMLControls::startForm("frmMain"); ?>
<?php clsHTMLControls::addTextBox"txtName""Enter your name..."NULLNULLNULLNULL"font-style: italic; color: #999999;" ); ?>
<?php clsHTMLControls::addButton"cmdAccept""Accept" ); ?>
<?php clsHTMLControls::addButton"cmdClear""Clear" ); ?>
<?php clsHTMLControls::endForm(); ?>

<?php clsHTMLControls::initControls(); ?>
</body>
</html>

As you can the syntax in index.php (where the form will be created) is clean, straight-forward and much simpler, as the hard coding is already been done for the end-developer...

EDIT: A bug fix ... :D

[attachment deleted by admin]
Title: Re: Call javascript functions
Post by: datahell on August 02, 2010, 22:56:27
Your initial idea is to create a general usage script that could attach automatically events to a form's elements without having to modify the html of the page. Right? But, as I see, you dont do that as you have to edit the page's header to add your "on event" functions.

What I dont like in your implementation is:
- You have too much javascript on the page's header.
- You dont provide a good solution for custom messages and validation for each element. To solve this you put the needed info inside js functions with a not reusable way. It is like transferring the HTML to javascript but without providing something new.
- The standard way would require less code than your implementation does and would be much faster. I am a great supporter of the moto "keep it simple and stupid".

Your implementation would have a meaning if you created a really re-usable and general js class that could handle any element by loading info from a library. Your main class should also create the "on event" functions. This is something that would be really interesting.
Title: Re: Call javascript functions
Post by: apkoutsou on August 02, 2010, 23:51:43
The whole idea started as a programming puzzle - just to fill my spare time - with no real purpose. I remembered the time when I programmed in VBasic: there you just declared an object and then the object was accessible straightforward, by its name, and all of its events where accessible by functions named like <object name>_<event type>, (in contrary to other languages where you have to manually connect the events to custom functions or search the object through an endless geneology of parents and children).

So I thought why not implement that kind of syntax into javascript ??

The answer to that question was the above code... If an element is declared, then all of its events are accessible through an <element name>_on<event type> function and the elements themselves by their name.

I agree that this kind of aproach is a bit wierd when you want to build a single web application, mostly due to the third reason you mentioned (the other two can be easily handled, as this was an example, not a complete solution); it's much easier, lighter and faster to connent the event function inline to the element declaration, than running a multiline function connected to all elements events...

But then again, if you wanted to build a general class or create a framework (where nobody knows from the beggining what elements will be declared) this approach can really be useful, making the development of any web application really simple...

I really don't know yet... Though puzzle is solved, I can think many interesting uses for this one...
Title: Re: Call javascript functions
Post by: datahell on August 03, 2010, 09:09:08
The whole thing is interesting and a programming puzzle as you say.
To give you an idea on how I would built that if I followed your way of thinking I would initialize an instance of the main class for each form and pass form elements as parameters to the js object. Then the class would create the "on event" functions for each triggered element and attach them to the corresponding elements.
Title: Re: Call javascript functions
Post by: apkoutsou on August 03, 2010, 16:20:52
Your hint is great!

The way I see it the class would basically:
1. Initialize on a form
2. Loop through-out the form to get its elements [why should you pass elements as parameters ??]
3. Search which event functions (according to the desired name convention) exist [the event functions could be in an external js file]
4. Connect each element to the found event functions
This way there is no need to search on-the-fly for the right event function (as on the code above). The only disadvantage is that this way an element will be accessible through its parent (form) and not straightforward, but this may be easily overcome...

Further more, another general and extendable input element class could be created, to hold basic functionality for all and each element. This way also advanced functionality (such as basic visual effects) could be easily implemented for each type of element... For example a resizable textarea...

I'll give it a try as soon as I publish the new version of EventCalendar and its update patch - propably this weekend...
Title: Re: Call javascript functions
Post by: datahell on August 03, 2010, 18:34:59
Not exactly. I did nt mean to loop through the form. I told you to create an object for each form (this way you can support unlimited forms per page) and pass the form elements you want to be triggered as parameters to the object. Each parameter can be an array containing info such as the element id, the events you want to trigger, etc. You can get the element type (text, select, etc) from javascript so you dont have to pass this info.