i like to pass to a function pointer to function that the addEventListener will use .
if you run this you will get an error .
what is the "Javascript" way to pass a function pointer ( don't know how to call it ) to addEventListener ?
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<title>Title</title>
<style>
</style>
<script>
var data = {
"foo1" : "aaa",
"foo2" : "bbb",
"foo3" : "ccc"
}
var createLabel = function(mykey,func) {
var label = document.createElement('label');
label.innerHTML = mykey;
label.id = "lbl_"+mykey;
label.addEventListener("click", () =>{
self.func(mykey);
}, false);
document.getElementById("container2").appendChild(label);
var br = document.createElement('br');
document.getElementById("container2").appendChild(br);
};
var popolateDS = function() {
self = this;
var i = 0;
for(var key in data) {
(function () {
var mykey = key;
if (data.hasOwnProperty(key)) {
if(i==0) {
createLabel(key,dsOnClick1);
i++;
}
createLabel(key,dsOnClick2);
}
}()); // immediate invocation
}
}
var dsOnClick1 = function(key) {
alert("dsOnClick1 "+key);
}
var dsOnClick2 = function(key) {
alert("dsOnClick2 "+key);
}
</script>
</head>
<body>
<div id="container2">
</div>
<button onclick="popolateDS()">click</button>
</body>
</html>
You don't need to refer to this as self.func, you can just call func like below and it'll work as expected:
label.addEventListener("click", () =>{
func(mykey);
}, false);
Related
I am working on a chrome extension where keywords are populated in search from MYSQL database
my popup.js :
// UI creation
!function() {
"use strict";
/*|================================================================|*/
/*| UI creation |*/
/*|================================================================|*/
var initSectionByBgColorFromTemplate = function (sectionNodeTemplate, bgColorCode, wordGroupsDict) {
var sectionNode = sectionNodeTemplate.cloneNode(true);
var handlingIndex = 0;
var classNames = ["pure-toggle-checkbox", "pure-toggle", "color-header", "remove-group-button", "highlight-words"];
var toggleCheckbox = sectionNode.getElementsByClassName(classNames[handlingIndex])[0];
toggleCheckbox.id = classNames[handlingIndex].concat("-", bgColorCode);
toggleCheckbox.dataset.bgColor = bgColorCode;
toggleCheckbox.checked = wordGroupsDict[bgColorCode].isOn;
toggleCheckbox.addEventListener("change", wordGroupToogleHandlerFactory(wordGroupsDict));
handlingIndex++;
var toggleLabel = sectionNode.getElementsByClassName(classNames[handlingIndex])[0];
toggleLabel.id = classNames[handlingIndex].concat("-", bgColorCode);
toggleLabel.htmlFor = toggleCheckbox.id;
handlingIndex++;
var header = sectionNode.getElementsByClassName(classNames[handlingIndex])[0];
header.style.backgroundColor = "#".concat(bgColorCode);
header.textContent = bgColorCode;
handlingIndex++;
var removeButton = sectionNode.getElementsByClassName(classNames[handlingIndex])[0];
removeButton.dataset.bgColorCode = bgColorCode;
removeButton.addEventListener("click", removeGroupHandlerFactory(wordGroupsDict, sectionNode));
handlingIndex++;
var textarea = sectionNode.getElementsByClassName(classNames[handlingIndex])[0];
textarea.id = classNames[handlingIndex].concat("-", bgColorCode);
textarea.dataset.bgColor = bgColorCode;
textarea.value = wordGroupsDict[bgColorCode].words.join(" ");
textarea.addEventListener("blur", wordListChangeHandlerFactory(wordGroupsDict));
handlingIndex++;
return sectionNode;
};
var mainBlock = document.getElementById("mainBlock");
var sessionTemplate = mainBlock.getElementsByTagName("section")[0];
var newGroupForm = document.getElementById("new-group-form");
var colorInputBox = document.getElementById("new-group-color");
/*|================================================================|*/
/*| load UI data and event binding |*/
/*|================================================================|*/
var getDefaultWordGroup = function (groupName) {
return {
groupName: groupName,
isOn: false,
words: []
};
};
var createNewGroupInDict = function (wordGroupsDict, groupName) {
var wordGroup = wordGroupsDict[groupName];
if (!wordGroup) {
wordGroup = getDefaultWordGroup(groupName);
wordGroupsDict[groupName] = wordGroup;
}
};
var saveAndSendMsg = function (wordGroupsDict) {
chrome.storage.sync.set({
wordGroupsDict: wordGroupsDict
}, function () {
// console.log("wordGroupsDict saved");
});
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var messageBody = wordGroupsDict;
chrome.tabs.sendMessage(tabs[0].id, messageBody, function(response) {
// console.log(response.content);
});
});
};
var wordGroupToogleHandlerFactory = function (wordGroupsDict) {
return function (event) {
var groupName = event.target.dataset.bgColor;
var wordGroup = wordGroupsDict[groupName];
wordGroup.isOn = event.target.checked;
saveAndSendMsg(wordGroupsDict);
};
};
var wordListChangeHandlerFactory = function (wordGroupsDict) {
return function (event) {
var groupName = event.target.dataset.bgColor;
var wordGroup = wordGroupsDict[groupName];
wordGroup.words = event.target.value.match(/[^\s]+/g) || [];
saveAndSendMsg(wordGroupsDict);
};
};
var removeGroupHandlerFactory = function (wordGroupsDict, sectionNode) {
return function (event) {
sectionNode.parentElement.removeChild(sectionNode);
delete wordGroupsDict[event.target.dataset.bgColorCode];
saveAndSendMsg(wordGroupsDict);
};
};
/*|================================================================|*/
/*| load extension settings |*/
/*|================================================================|*/
chrome.storage.sync.get('wordGroupsDict', function (wordGroupsDict) {
// I just dont know how chrome.storage.sync works...
// + nothing inside, return {}
// + find the key, return {key: value}
wordGroupsDict = wordGroupsDict.wordGroupsDict || wordGroupsDict;
/*|================================================================|*/
/*| popup UI and event binding |*/
/*|================================================================|*/
// use default for 1st time
var colorGroups = Object.keys(wordGroupsDict);
if (colorGroups.length === 0) {
colorGroups = ["C72E04", "FA9507", "CACF44", "27AB99"].slice(1);
colorGroups.forEach( colorGroup => createNewGroupInDict(wordGroupsDict, colorGroup) );
}
/*
if (colorGroups.length === 0) {
var valNew = [];
var array = [];
$.ajax({
type: 'post',
url: 'http://localhost/chrome-highlight-extension-master/js/loadcolor.php',
data: {
},
success: function (response) {
var res = $.trim(response);
valNew = res.split(',');
for(var i=0;i<(valNew.length);i++){
colorGroups.push(valNew[i]);
}
colorGroups.forEach( colorGroup => createNewGroupInDict(wordGroupsDict, colorGroup) );
}
});
} */
// remove template and append initialized sections
mainBlock.removeChild(sessionTemplate);
colorGroups.forEach(function (bgc) {
mainBlock.appendChild(initSectionByBgColorFromTemplate(sessionTemplate, bgc, wordGroupsDict));
});
newGroupForm.addEventListener("submit", function (event) {
event.preventDefault();
if (colorInputBox.value && colorInputBox.value.length > 0 && colorInputBox.checkValidity()) {
console.log("submit OK");
createNewGroupInDict(wordGroupsDict, colorInputBox.value);
mainBlock.appendChild(initSectionByBgColorFromTemplate(sessionTemplate, colorInputBox.value, wordGroupsDict));
}
console.log("submit");
});
colorInputBox.addEventListener("onload", function (event) {
if (event.target.checkValidity()) {
event.target.style.backgroundColor = "#".concat(event.target.value);
} else {
event.target.style.backgroundColor = "white";
}
});
});
}();
In the above code I have some static colors
colorGroups = ["C72E04", "FA9507", "CACF44", "27AB99"];
But I want these colors from MYSQL db , so i used ajax to get colors (that part of the code has been commented )
if (colorGroups.length === 0) {
var valNew = [];
var array = [];
$.ajax({
type: 'post',
url: 'http://localhost/chrome-highlight-extension-master/js/loadcolor.php',
data: {
},
success: function (response) {
var res = $.trim(response);
valNew = res.split(',');
for(var i=0;i<(valNew.length);i++){
colorGroups.push(valNew[i]);
}
colorGroups.forEach( colorGroup => createNewGroupInDict(wordGroupsDict, colorGroup) );
}
});
}
In the ajax code i am able to fetch colors from my table but the problem is with createNewGroupInDict
this is the error I am facing
Error in response to storage.get: TypeError: Cannot read property 'addEventListener' of null
at Object.callback (chrome-extension://pgommpfcheidcagdchjjdhffidaefhaf/js/popup.js:159:16)
at chrome-extension://pgommpfcheidcagdchjjdhffidaefhaf/js/popup.js:113:22
at chrome-extension://pgommpfcheidcagdchjjdhffidaefhaf/js/popup.js:176:2
popup.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Keyword Page</title>
<link href="css/popup.css" media="screen" rel="stylesheet" type="text/css">
<link href="css/toggle.css" media="screen" rel="stylesheet" type="text/css">
</head>
<body>
<main id="mainBlock">
<div class="button-block">
<form id="new-group-form">
<input id="new-group-color" type="text" pattern="[0-9a-fA-f]{6}" title="hex color code" />
<input id="add-button" class="button" type="submit" value="+" title="add group" />
</form>
</div>
<section>
<span class="toggle">
<input type="checkbox" class="pure-toggle-checkbox" hidden />
<label class="pure-toggle flip" for="pure-toggle-">
<div class="fontawesome-remove">OFF</div>
<div class="fontawesome-ok">ON</div>
</label>
</span>
<header class="color-header">C72E04</header>
<button class="remove-group-button button">x</button>
<textarea class="highlight-words" placeholder="(separated by a space)" > </textarea>
</section>
</main>
<script src="js/popup.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/custom.js"></script>
</body>
</html>
I am trying to make a pure js mvc app where I update an h1 with the text of an input field. I got to the point that the the value of the input in the model can be logged nicely but for some reason the h1 is not changing at all.
Could you give me some help that why is that and how to solve it?
my code:
window.onload = function() {
var model = new Model();
var controller = new Controller(model);
var view = new View(controller);
};
function Model() {
this.inputtext = "zzzzz";
this.heading = this.inputtext;
console.log('model called');
};
function Controller(model) {
var controller = this;
this.model = model;
this.handleEvent = function(e) {
switch (e.type) {
case "click":
controller.clickHandler(e.target);
break;
case "input":
controller.keyupHandler(e.target);
break;
default:
console.log(e.target);
}
}
this.getModelHeading = function() {
console.log("from getmodel: " + controller.model.inputtext + "heading " + controller.model.heading);
return controller.model.heading;
}
this.keyupHandler = function(target) {
controller.model.inputtext = target.value;
controller.getModelHeading();
}
console.log('controller called');
};
function View(controller) {
this.controller = controller;
this.heading = document.getElementById("heading");
this.heading.innerHTML = controller.getModelHeading();
this.inputtext = document.getElementById("inputtext");
this.inputtext.addEventListener('input', controller);
console.log('view called');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" : content="width=device-width">
<title>Vanilla MVC Framework</title>
<script type="text/javascript" src="./Config.js"></script>
<script type="text/javascript" src="./Model.js"></script>
<script type="text/javascript" src="./Controller.js"></script>
<script type="text/javascript" src="./View.js"></script>
</head>
<body>
<input id='inputtext' /></input>
<h1 id='heading'></h1>
</body>
</html>
You need to link the view to the controller, then modify the view from the controller.
window.onload = function() {
var model = new Model();
var controller = new Controller(model);
var view = new View(controller);
};
function Model() {
this.inputtext = "zzzzz";
this.heading = this.inputtext;
console.log('model called');
};
function Controller(model) {
var controller = this;
this.model = model;
this.handleEvent = function(e) {
switch (e.type) {
case "click":
controller.clickHandler(e.target);
break;
case "input":
controller.keyupHandler(e.target);
break;
default:
console.log(e.target);
}
}
this.getModelHeading = function() {
// console.log("from getmodel: " + controller.model.inputtext + "heading " + controller.model.heading);
controller.model.heading = controller.model.inputtext;
return controller.model.heading;
}
this.keyupHandler = function(target) {
controller.model.inputtext = target.value;
controller.view.heading.innerHTML=controller.getModelHeading();
}
console.log('controller called');
};
function View(controller) {
this.controller = controller;
this.heading = document.getElementById("heading");
this.heading.innerHTML = controller.getModelHeading();
this.inputtext = document.getElementById("inputtext");
this.inputtext.addEventListener('input', controller);
controller.view = this;
console.log('view called');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" : content="width=device-width">
<title>Vanilla MVC Framework</title>
<script type="text/javascript" src="./Config.js"></script>
<script type="text/javascript" src="./Model.js"></script>
<script type="text/javascript" src="./Controller.js"></script>
<script type="text/javascript" src="./View.js"></script>
</head>
<body>
<input id='inputtext' />
<h1 id='heading'></h1>
</body>
</html>
You update the h1 element only in the constructor of View class.
In keyUp event handler you update the only model but you haven't reassigned the view.heading.innerHtml value.
Only your View should know about where in DOM to display a model.property. Therefore, my suggestion to you add this code in your View:
function View(controller) {
var _self = this;
this.controller = controller;
this.heading = document.getElementById("heading");
updateHeading.call(_self);
this.inputtext = document.getElementById("inputtext");
this.inputtext.addEventListener('input', function(e){
controler.handleEvent(e);
updateHeading.call(_self);
});
console.log('view called');
function updateHeading(){
this.heading.innerHTML = controller.getModelHeading();
}
}
In the following code I get an error: TypeError: i.Print is not a function when the button is clicked. What is the cause of this error, and how do I fix it? Using Firefox debugger when I look at the value of i in the button's click handler, I see that i.prototype.Print has value Outer/Inner.prototype.Print().
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<p id="prn">Value here</p>
<button id='btn'>Print</button>
<script src="https://code.jquery.com/jquery-1.12.4.min.js">
</script>
<script>
function TestObject(i)
{
$("#btn").on("click", function() {
i.Print();
i.Change(Math.random() * 10 + 5);
});
}
function TestPrototype()
{
var o = function Outer() {
function Inner(v)
{
var iv = v;
function print()
{
$("#prn").text(iv);
}
};
Inner.prototype.Print = function() {
print();
console.log(iv);
};
Inner.prototype.Change = function(nv) {
iv = nv;
};
return {
getInner : function(v) {
var i = Inner;
i(v);
return i;
}
};
}();
var i1 = o.getInner(10);
TestObject(i1);
}
;(function() {
TestPrototype();
}());
</script>
</body>
</html>
You need to create an object using the constructor,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<p id="prn">Value here</p>
<button id='btn'>Print</button>
<script src="https://code.jquery.com/jquery-1.12.4.min.js">
</script>
<script>
function TestObject(i)
{
$("#btn").on("click", function() {
i.Print();
i.Change(Math.random() * 10 + 5);
});
}
function TestPrototype()
{
var o = function Outer() {
function Inner(v)
{
// instatiate member variables
this.iv = v;
this.print = print;
function print()
{
$("#prn").text(this.iv);
}
};
Inner.prototype.Print = function() {
// access member variable
console.log(this.iv);
this.print();
print();
};
Inner.prototype.Change = function(nv){
iv = nv;
};
return {
getInner : function(v) {
var i = Inner;
return new i(v);
}
};
}();
var i1 = o.getInner(10);
TestObject(i1);
}
;(function() {
TestPrototype();
}());
</script>
</body>
</html>
I have a list of elements. When I click an up or down icon, I would like the list to rearrange itself and, finally, for the app to rerender itself so I can see the change reflected in the DOM.
Changing list position works. I'm running into issues when I try to run the refreshState method. I'm passing the function as a property of the child but calling that property returns undefined function.
Q: How do I call a component's method from its child component?
Code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script src="http://fb.me/react-with-addons-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
</head>
<body>
<style>
/* CSS */
span {
margin:0 0 0 10px;
}
</style>
<div id="app"></div>
<script type="text/jsx">
// React
var _data = ['Red', 'Blue', 'Green'];
function getState() {
return {
colors: _data,
}
};
Array.prototype.swap = function(a, b) {
var temp = this[a];
this[a] = this[b];
this[b] = temp;
};
var App = React.createClass({
getInitialState: function() {
return getState();
},
render: function() {
var colors = this.state.colors.map(function(color) {
return (
<Color name={color} refreshState={this.refreshState} />
)
});
return (
<ul>{colors}</ul>
)
},
refreshState: function() {
return this.setState(getState());
},
});
var Color = React.createClass({
moveUp: function() {
var current = _data.indexOf(this.props.name),
above = current - 1;
if (above >= 0) {
_data.swap(current, above);
}
return this.props.refreshState();
},
moveDown: function() {
var current = _data.indexOf(this.props.name),
below = current + 1;
if (below < _data.length) {
_data.swap(current, below);
}
return this.props.refreshState();
},
render: function() {
return (
<li>
<strong>{this.props.name}</strong>
<span onClick={this.moveUp}>^</span>
<span onClick={this.moveDown}>v</span>
</li>
)
},
});
React.render(<App />, document.getElementById('app'));
</script>
</body>
</html>
Noticed you have solved the question, but thought I'd mention that you can pass a scope to .map which means no need to cache scope for the purpose you describe:
this.state.colors.map(function(color) {
return (
<Color
key={_data.indexOf(color)}
name={color}
refresh={this.refreshState}
/>
)
}, this); // pass the scope to .map
I was trying to call this.refreshState within the map method. This, of course, does not have the same scope as the render method. The solution was to store the scope in a variable:
var refresh = this.refreshState;
Then use that variable within the map method:
... refreshState={refresh} ...
Always be aware of your scope!
If you have multiple functions that aren't within the local scope then you can store this in a variable.
var self = this;
z.map(function(arg) {
x={self.refreshState} y={self.otherThing}
And for the curious, the finished result:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script src="http://fb.me/react-with-addons-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
</head>
<body>
<style>
/* CSS */
span {
margin:0 0 0 10px;
}
</style>
<div id="app"></div>
<script type="text/jsx">
// React
var _data = ['Red', 'Blue', 'Green'];
function getState() {
return {
colors: _data,
}
};
Array.prototype.swap = function(a, b) {
var temp = this[a];
this[a] = this[b];
this[b] = temp;
};
var App = React.createClass({
getInitialState: function() {
return getState();
},
refreshState: function() {
return this.setState(getState());
},
render: function() {
var self = this;
var colors = this.state.colors.map(function(color) {
return (
<Color
key={_data.indexOf(color)}
name={color}
refresh={self.refreshState}
/>
)
});
return (
<ul>{colors}</ul>
)
},
});
var Color = React.createClass({
propTypes: {
name: React.PropTypes.string,
refresh: React.PropTypes.func.isRequired,
},
moveUp: function() {
var current = _data.indexOf(this.props.name),
above = current - 1;
if (above >= 0) {
_data.swap(current, above);
}
return this.props.refresh();
},
moveDown: function() {
var current = _data.indexOf(this.props.name),
below = current + 1;
if (below < _data.length) {
_data.swap(current, below);
}
return this.props.refresh();
},
render: function() {
return (
<li>
<strong>{this.props.name}</strong>
<span onClick={this.moveUp}>^</span>
<span onClick={this.moveDown}>v</span>
</li>
)
},
});
React.render(<App />, document.getElementById('app'));
</script>
</body>
</html>
I would like to create a method on the singleton dp.DatapodManager so that it loads the data once via $.post, then I can use that data by calling methods on the singleton.
The output of the following code is:
111
222
test data
What do I have to do to get dp.DatapodManager.loadDirectly(); to add the contents of the text file to div#content?
<html>
<head>
<title>test load</title>
<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('div#content').append('<p>1111</p>');
$('div#content').append('<p>222</p>');
$('div#content').append(dp.DatapodManager.getTestData());
dp.DatapodManager.loadDirectly(function(html) {
$('div#content').append(html);
});
});
var dp = dp || {
VERSION : '0.00.05'
};
dp.DatapodManager = (function() {
return {
loadDirectly: function(callback) {
dp.qsys.loadDataFromExternalWebsite(function(stringBlock) {
var lines = dp.qstr.convertStringBlockToLines(stringBlock);
var html = dp.qstr.appendBrToLines(lines);
callback(html); //never executes, cannot set breakpoint here in firebug
});
callback('<p>this is returned</p>');
},
getTestData: function() {
return 'test data';
}
}
}());
dp.qsys = {
loadDataFromExternalWebsite : function(callback) {
url = 'http://localhost/webs/dpjs/data/data.txt';
var json = '';
(function() {
var json = null;
$.post(url, {}, function(jsonString) {
callback(jsonString);
});
return json;
})();
}
};
dp.qstr = {
convertStringBlockToLines: function(block, trimLines) {
var trimLines = trimLines || true;
var lines = block.split(dp.qstr.NEW_LINE());
if(trimLines && lines.length > 0) {
for(x=0; x<lines.length; x++) {
lines[x] = lines[x].trim();
}
}
return lines;
},
NEW_LINE: function() {
return '\r\n';
},
appendBrToLines: function(lines) {
var r = '';
if(lines.length > 0) {
for(x=0; x<lines.length; x++) {
r += lines[x] + '<br/>';
}
}
return r;
}
};
</script>
</head>
<body>
<div id="content"></div>
</body>
</html>