0

I'm trying to call a function by clicking an HTML link on a surface. This doesn't work as intended:

define(function(require, exports, module) {
    var Engine = require("famous/core/Engine");
    var Surface = require("famous/core/Surface");
    var mainContext = Engine.createContext();

    var surf = new Surface({
        size: [100, 50],
        content: '<a href="#" onclick="test()">Click me</a>',
        properties: {
            backgroundColor: '#00FF00'
        }
    });

    function test(){
        alert('Test!');
    }

    mainContext.add(surf);
});
var test = function(){
    alert('Wrong call...');
}

I want it to call the first "test()" function, but all it does is call the outer one.

To get it, I tried something like this:

var testObject = define(function(require, exports, module) {
(...)
    var surf = new Surface({
        size: [100, 50],
        content: '<a href="#" onclick="testObject.test()">Click me</a>',
        properties: {
            backgroundColor: '#00FF00'
        }
    });
(...)
};

However that didn't work either. Any ideas on how to get it to call the right function?

Mike
  • 13
  • 4

2 Answers2

0

Your issue is to do with how JavaScript handles scope.

While your inner ''test'' function is inside the scope of "define," all that the html element that's rendered out by Famo.us can see is the outer ''test'' function.

The following is more eloquent way of binding events to a surface:

var surf = new Surface({
    size: [100, 50],
    content: '<p>Click me</p>',
    properties: {
        backgroundColor: '#00FF00'
    }
});

test = function() { 
    alert("Test!"); 
};

surf.on("click", test());

UPDATE FOLLOWING COMMENT

Below is an alternative, but bad practice, you could also do something like this. It's not bad per-se, but it's not really a best practice:

var surf = new Surface({
    size: [100, 50],
    content: '<p onclick="|*Your Exported Module Name*|.test()">Click me</p>',
    properties: {
        backgroundColor: '#00FF00'
    }
});

surf.test = function() { 
    alert("Test!"); 
};

As you can see in the above, by explicitly giving the surf variable ownership of the test() method, it saves us maybe two lines of readable JS and basically marries the markup to business logic code. Because you're using RequireJS and the onclick attribute in the DOM element can only easily see things that exist in global Javascript scope, we have to drill down all the way into the correct variable of the correct RequireJS module in order to access it's method.

If the surf variable were ever deleted or modified in any way (don't get me started) you're code will easily stop working correctly now, as your html element depends on the existence of a javascript variable called surf and a method called test and thus your app becomes a maintenance nightmare...

So stay safe. Famo.us is a Javascript Rendering Engine that just so happens to render to DOM, but that doesn't necessarily imply all the old DOM principles can still be used and remain elegant like they are in traditional web pages. Good luck :)

Kraig Walker
  • 812
  • 13
  • 25
  • If it were only one link on that surface, then this would surely be the way to go. My surface will have many links in its content though, with various functions and parameters. That's why I want(/need?) to use Hyperlinks instead of just handling the click-Event on the surface. – Mike Jan 14 '15 at 21:09
  • An anchor tag is intended for linking to different areas of content, usually pages or even section ids of content in the current page. The latter idea doesn't really apply to famo.us as everything is absolutely positioned. – Kraig Walker Jan 14 '15 at 21:27
0

Capture the click event from the target of the click rather than the captured click event of the Surface. Use an identifier (id) rather than a method function call to identify the link function we want to call.

Here is the full code: Example on jsBin

First we track the clicks within our target surface and call the function to check if it is a link. If we clicked on a link we will emit an event passing the target href.

var surf = new Surface({
    size: [100, 200],
    content: '<a id="test" href="#test">Click me</a><br/><a id="test1" href="#test1">Click me</a><br/><a id="test2" href="#test2">Click me</a><br/><a id="test3" href="#test3">Click me</a>',
    properties: {
        backgroundColor: '#00FF00',
        textAlign: 'center',
        borderRadius: '10px',
        lineHeight: '40px'
    }
});

surf.clickNullifier = function (e) {
    if (e.target && e.target.nodeName == 'A' && e.target.id) {
        console.log('id', e.target.id);
        console.log('href', e.target.href);
      switch (e.target.id) {
        case 'test':
          test.call(this, 'called test');
          break;
        case 'test1':
          test.call(this, 'called test1' );
          break;
        case 'test2':
          test.call(this, 'called test2');
          break;
        case 'test3':
          test.call(this, 'called test3');
          break;
        default:
      }
    }
    e.preventDefault();
    return false;
}.bind(this);

surf.on('deploy', function () {
    console.log('deploy',this._currentTarget);
    // sets up the click function on the surface DOM object
    this._currentTarget.onclick = this.clickNullifier;
});

function test(msg) {
    this.backSurface.setOptions({content: msg});
}
talves
  • 13,993
  • 5
  • 40
  • 63