strange javascript IE bug, variable in class clears - javascript

I've got a problem, so:
var pager = new Array();
var Imtech = {};
Imtech.Pager = function() {
this.paragraphs = '';
this.showPage = function() {
console.log(this.paragraphs.html());
}
}
What i do:
pager[0] = new Imtech.pager();
pager[0].paragraphs = $('some obj name here');
pager[0].showPage();
pager[0].showPage();
So, i ve got arr of paginating classes;
When i call method pager[0].showPage(); - its all ok
But when i try to make it again pager[0].showPage() - i got empty result...
Even got no ideas where is mistake; and var clears not only after console.log(), but after any operation...
How I decided problem:
So I made a very very bad version, but for ie it works fine;
The problem was, that after first usage of stored DOM element, all innerHTML cleared, but objects still existed; I decided to store innerHTML like a text string; smth like:
pager[0].innerHTML = $('some obj name here').html();
And when i need this inneHTML like obj I can make obj, so it's not brilliant variant, but in ie (& other browsers) works fine.

Related

JavaScript error with eventListener

I'm getting an error which I'm not quite sure what to make of. Anyway, before I go on to that, I found out about Unobtrusive JavaScript, at first I was just going to add an "OnClick" to my HTML but then found out that isn't a very good thing to do.
Anyway, so I did that and turned up with this code which isn't quite finished yet, but I wanted to try it out anyway before I went in and made any other changes.
window.onload = function findSubmitButton(){
var button = document.getElementsByClass("send_info").addEventListener("click", retrieveInputText());
}
function retrieveInputText(){
var inputArray = document.querySelectorAll("#container_id input[type=text]");
var finalArray;
for (var i in inputArray){
if(inputArray[i].type == "text"){
finalArray.push(i);
}
alert("done");
}
}
The error chrome's console gives me is this: Uncaught TypeError: undefined is not a functionfindInputs.js:5 findSubmitButton
There was also something I wanted to know, I want to be able to use this script with any other sort of input form, so instead of directly identifying the button for this page, I used a class identifier, this way, it works with any page. The only way there would be any issues would be if I had two buttons of the sort, as it is right now, any page with that sort of information only has one button for such procedures. I would appreciate if someone helped me out with this, I'm new to JavaScript.
getElementsByClassName(), returns an array, not an element so it does not have the addEventListener method, also you need to pass a function reference
window.onload = function findSubmitButton() {
var button = document.getElementsByClassName("send_info")[0].addEventListener("click", retrieveInputText);
}
Also you need to initialize the array var finalArray = [];
window.onload = function findSubmitButton() {
var button = document.querySelector(".send_info").addEventListener("click", retrieveInputText);
}
function retrieveInputText() {
var inputArray = document.querySelectorAll("#container_id input[type=text]");
var finalArray = [];
for (var i in inputArray) {
if (inputArray[i].type == "text") {
finalArray.push(i);
}
}
alert("done:" + finalArray);
}
Demo: Fiddle

Cannot add item to observableArray

This should be a pretty trivial question, but it's getting me crazy!
I't like step 1 of the tutorial, but I can't it to work.... so this is my code:
/* Model definition */
var PrivateSalesMenuModel = function () {
var self = this;
this.privateSales = ko.observableArray();
this.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
var PrivateSaleMenuItem = function (ps) {
this.title = ps.Description;
this.hasActual = ps.HasActual;
this.hasSale = ps.HasSale;
this.listaId = ps.ListaId;
this.isSelected = false;
};
/* end model definition*/
var privateSalesMenuModel = new PrivateSalesMenuModel();
ko.applyBindings(privateSalesMenuModel);
Pretty simple... I have an object that represent my model, that is a collection of others objects, called PrivateSaleMenuItem.
Problem is that addPrivateSale didn't work as expected. Somewhere in the code I do
privateSalesMenuModel.addPrivateSale(ps);
where ps is an object created by other JavaScript functions... anyway is exactaly the object I need in the constructor of PrivateSaleMenuItem, so it's consistency is not the problem.
The problem seems o be that self.privateSales.push(newPs); doesn't work... after that inocation, the number of privateSalesMenuModel.privateSales is still 0.
Why is that?
Edited
I put toghether an example in jsFiddle with this same exact code, and it works fine, so I suspect something in my page make the push method of observableArray stop working... how can I find out what it is?
ops... the link of jsfiddle http://jsfiddle.net/YBHf5/
It looks like you are mixing this and self in your code here:
var PrivateSalesMenuModel = function () {
var self = this;
this.privateSales = ko.observableArray();
this.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
Changing them to all use self fixes it like this:
var PrivateSalesMenuModel = function () {
var self = this;
self.privateSales = ko.observableArray();
self.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
Please see working fiddle here:
http://jsfiddle.net/YBHf5/1/
Problem solved.... it was a trivial problem indeeed, with a trivial solution: I just needed to push the external script declaration at the end of the page.
I had the script (in which the code provided on the question is present) at the top of the page, inside the body, but above anything else.
Just putting the script reference at the end of the body and everything works as expected again.
Sorry for wasting your time... but this little issue is very hard to find out, 'cause the reason is not clear at all while debugging

GUI Layer on limeJS

I'm working with limeJS trying to figure out the best way to put a GUI over limeJS. Here's a better explanation:
I've created two classes, one called 'SceneWithGui' and another called 'GuiOverlay'. My intention is that 'SceneWithGui' inherits from 'lime.Scene', adding a property and two methods:
guiLayer (which is a GuiOverlay object)
setGuiLayer
getGuiLayer
As follows:
//set main namespace
goog.provide('tictacawesome.SceneWithGui');
//get requirements
goog.require('lime.Scene');
goog.require('tictacawesome.GuiOverlay');
tictacawesome.SceneWithGui = function() {
lime.Node.call(this);
this.guiLayer = {};
};
goog.inherits(tictacawesome.SceneWithGui, lime.Scene);
tictacawesome.SceneWithGui.prototype.setGuiLayer = function (domElement){
this.guiLayer = new tictacawesome.GuiOverlay(domElement);
};
tictacawesome.SceneWithGui.prototype.getGuiLayer = function (){
return this.guiLayer;
};
The intention with 'GuiOverlay' is to make it hold the DOM element in a way that when the Scene or the Director get resized, it will too. For it, I just inherited from 'lime.Node', hoping it is something already set on it. My code:
//set main namespace
goog.provide('tictacawesome.GuiOverlay');
//get requirements
goog.require('lime.Node');
tictacawesome.GuiOverlay = function(domElement){
lime.Node.call(this);
this.setRenderer(lime.Renderer.DOM);
this.domElement = domElement;
};
goog.inherits(tictacawesome.GuiOverlay, lime.Node);
And that's basically the idea of it all. For better visualization, here's a sample use:
//set main namespace
goog.provide('tictacawesome.menuscreen');
//get requirements
goog.require('lime.Director');
goog.require('lime.Scene');
goog.require('lime.Layer');
goog.require('lime.Sprite');
goog.require('lime.Label');
goog.require('tictacawesome.SceneWithGui');
tictacawesome.menuscreen = function(guiLayer) {
goog.base(this);
this.setSize(1024,768).setRenderer(lime.Renderer.DOM);
var backLayer = new lime.Layer().setAnchorPoint(0, 0).setSize(1024,768),
uiLayer = new lime.Layer().setAnchorPoint(0, 0).setSize(1024,768),
backSprite = new lime.Sprite().setAnchorPoint(0, 0).setSize(1024,768).setFill('assets/background.png');
backLayer.appendChild(backSprite);
this.appendChild(backLayer);
lime.Label.installFont('Metal', 'assets/metalang.ttf');
var title = new lime.Label().
setText('TicTacAwesome').
setFontColor('#CCCCCC').
setFontSize(50).
setPosition(1024/2, 100).setFontFamily('Metal');
uiLayer.appendChild(title);
this.appendChild(uiLayer);
this.setGuiLayer(guiLayer);
};
goog.inherits(tictacawesome.menuscreen, tictacawesome.SceneWithGui);
It all looks pretty on code, but it just doesn't work (the scene doesn't even get displayed anymore). Here are the errors I'm getting through Chrome console:
Uncaught TypeError: Cannot read property 'style' of undefined
director.js:301
Uncaught TypeError: Cannot read property
'transform_cache_' of undefined
I'm not really experienced in JS, so the only clue I found is that this happens when 'tictacawesome.menuscreen' reaches 'goog.base(this)'.
UPDATE: After some messing with the code, I'm able to pass the previous problems I had (before this edit), and now I stumble on the ones cited above. When it reaches directior.js:301, the line is scene.domElement.style['display']='none';. scene exists, but scene.domElement doesn't. Did I somehow fucked up the domElement with my guiOverlay?
Any ideas? Any obvious flaws on my design? Am I on the right path?
Thanks.
Your SceneWithGui extends lime.Scene but you call lime.Node.call(this); in SceneWithGui.
The code in SceneWithGui should look like this:
//set main namespace
goog.provide('tictacawesome.SceneWithGui');
//get requirements
goog.require('lime.Scene');
goog.require('tictacawesome.GuiOverlay');
tictacawesome.SceneWithGui = function() {
lime.Scene.call(this);//This was lime.Node.call(this);
this.guiLayer = {};
};
goog.inherits(tictacawesome.SceneWithGui, lime.Scene);
tictacawesome.SceneWithGui.prototype.setGuiLayer = function (domElement){
this.guiLayer = new tictacawesome.GuiOverlay(domElement);
};
tictacawesome.SceneWithGui.prototype.getGuiLayer = function (){
return this.guiLayer;
};
Can find it out quite quickly when creating a normal scene with var scene = new lime.Scene() breakpoint at that line and stepping into it.
It'll take you to scene.js and then set a breakpoint at lime.Node.call(this);
Then you would have noticed when creating scene = new tictacawesome.menuscreen(); that that breakpoint never hits.

How can I refresh a stored and snapshotted jquery selector variable

I ran yesterday in a problem with a jquery-selector I assigned to a variable and it's driving me mad.
Here is a jsfiddle with testcase:
assign the .elem to my obj var
log both lengths to the console. Result => 4
Remove #3 from the DOM
log obj to the console => the removed #3 is still there and the length is still 4.
I figured out that jquery query is snapshotted? to the variable and can't?won't? be updated
log .elem to the console.. yep Result => 3 and the #3 is gone
Now I update .elem with a new width of 300
logging obj & obj.width gives me 300.. So the snapshot has been updated ? What's interesting is that 3 of the 4 divs have the new width, but the removed #3 doesn't...
Another test: Adding a li element to the domtree and logging obj and .elem.
.elem does have the new li and obj doesn't, because it's still the old snapshot
http://jsfiddle.net/CBDUK/1/
Is there no way to update this obj with the new content?
I don't want to make a new obj, because in my application there is a lot information saved in that object, I don't want to destroy...
Yeah, it's a snapshot. Furthermore, removing an element from the page DOM tree isn't magically going to vanish all references to the element.
You can refresh it like so:
var a = $(".elem");
a = $(a.selector);
Mini-plugin:
$.fn.refresh = function() {
return $(this.selector);
};
var a = $(".elem");
a = a.refresh();
This simple solution doesn't work with complex traversals though. You are going to have to make a parser for the .selector property to refresh the snapshot for those.
The format is like:
$("body").find("div").next(".sibling").prevAll().siblings().selector
//"body div.next(.sibling).prevAll().siblings()"
In-place mini-plugin:
$.fn.refresh = function() {
var elems = $(this.selector);
this.splice(0, this.length);
this.push.apply( this, elems );
return this;
};
var a = $(".elem");
a.refresh() //No assignment necessary
I also liked #Esailija solution, but seems that this.selector has some bugs with filter.
So I modified to my needs, maybe it will be useful to someone
This was for jQuery 1.7.2 didn`t test refresh on filtered snapshots on higher versions
$.fn.refresh = function() { // refresh seletor
var m = this.selector.match(/\.filter\([.\S+\d?(\,\s2)]*\)/); // catch filter string
var elems = null;
if (m != null) { // if no filter, then do the evarage workflow
var filter = m[0].match(/\([.\S+\d?(\,\s2)]*\)/)[0].replace(/[\(\)']+/g,'');
this.selector = this.selector.replace(m[0],''); // remove filter from selector
elems = $(this.selector).filter(filter); // enable filter for it
} else {
elems = $(this.selector);
}
this.splice(0, this.length);
this.push.apply( this, elems );
return this;
};
Code is not so beautiful, but it worked for my filtered selectors.
Clean and generic solution worked properly with jQuery 3.4.1:
My solution is to do the following:
Intercept the selector at the time of jQuery object initialization and in the same time maintain all other jQuery functionalities transparently all this using inheritance
Build refresh plugin that make use of the new "selector" property we added during initialization
Definition:
$ = (function (originalJQuery)
{
return (function ()
{
var newJQuery = originalJQuery.apply(this, arguments);
newJQuery.selector = arguments.length > 0 ? arguments[0] : null;
return newJQuery;
});
})($);
$.fn = $.prototype = jQuery.fn;
$.fn.refresh = function ()
{
if (this.selector != null && (typeof this.selector === 'string' || this.selector instanceof String))
{
var elems = $(this.selector);
this.splice(0, this.length);
this.push.apply(this, elems);
}
return this;
};
Usage:
var myAnchors = $('p > a');
//Manipulate your DOM and make changes to be captured by the refresh plugin....
myAnchors.refresh();
//Now, myAnchors variable will hold a fresh snapshot
Note:
As optimization, object selectors don't need refresh as they are pass by reference by nature so, in refresh plugin, we only refresh if the selector is a string selector not object selector for clarification, consider the following code:
// Define a plain object
var foo = { foo: "bar", hello: "world" };
// Pass it to the jQuery function
var $foo = $( foo );
// Test accessing property values
var test1 = $foo.prop( "foo" ); // bar
// Change the original object
foo.foo = "koko";
// Test updated property value
var test2 = $foo.prop( "foo" ); // koko
Jquery .selector is deprecated, it's better to remeber string with selector value to some variable at the moment when you assign
function someModule($selector, selectorText) {
var $moduleSelector = $selector;
var moduleSelectorText = selectorText;
var onSelectorRefresh = function() {
$moduleSelector = $(moduleSelectorText);
}
}
https://api.jquery.com/selector/
You can also return the JQuery selector in a function, and save this function into the variable. Your code will look a bit different but it works. Every time when you execute the function, your jquery selector will search the DOM again.
In this example I used an arrow function without brackets which will return whatever is next to arrow. In this case it will return the JQuery collection.
const $mySelector = () => $('.selector');
console.log($mySelector().last().text());
$('.parent').append('<li class="selector">4</li>')
console.log($mySelector().last().text()); //RETURNS 4 not 3
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="parent">
<li class="selector">1</li>
<li class="selector">2</li>
<li class="selector">3</li>
</ul>
If you use remove() it will remove only a part of the DOM but not all the children or related, instead if you use empty() on the element the problem is gone.
E.G.:
$('#parent .child).find('#foo').empty();
Maybe it can be useful to someone!

Functional object basics. How to go beyond simple containers?

On the upside I'm kinda bright, on the downside I'm wracked with ADD. If I have a simple example, that fits with what I already understand, I get it. I hope someone here can help me get it.
I've got a page that, on an interval, polls a server, processes the data, stores it in an object, and displays it in a div. It is using global variables, and outputing to a div defined in my html. I have to get it into an object so I can create multiple instances, pointed at different servers, and managing their data seperately.
My code is basically structured like this...
HTML...
<div id="server_output" class="data_div"></div>
JavaScript...
// globals
var server_url = "http://some.net/address?client=Some+Client";
var data = new Object();
var since_record_id;
var interval_id;
// window onload
window.onload(){
getRecent();
interval_id = setInterval(function(){
pollForNew();
}, 300000);
}
function getRecent(){
var url = server_url + '&recent=20';
// do stuff that relies on globals
// and literal reference to "server_output" div.
}
function pollForNew(){
var url = server_url + '&since_record_id=' + since_record_id;
// again dealing with globals and "server_output".
}
How would I go about formatting that into an object with the globals defined as attributes, and member functions(?) Preferably one that builds its own output div on creation, and returns a reference to it. So I could do something like...
dataOne = new MyDataDiv('http://address/?client');
dataOne.style.left = "30px";
dataTwo = new MyDataDiv('http://different/?client');
dataTwo.style.left = "500px";
My code is actually much more convoluted than this, but I think if I could understand this, I could apply it to what I've already got. If there is anything I've asked for that just isn't possible please tell me. I intend to figure this out, and will. Just typing out the question has helped my ADD addled mind get a better handle on what I'm actually trying to do.
As always... Any help is help.
Thanks
Skip
UPDATE:
I've already got this...
$("body").prepend("<div>text</div>");
this.test = document.body.firstChild;
this.test.style.backgroundColor = "blue";
That's a div created in code, and a reference that can be returned. Stick it in a function, it works.
UPDATE AGAIN:
I've got draggable popups created and manipulated as objects with one prototype function. Here's the fiddle. That's my first fiddle! The popups are key to my project, and from what I've learned the data functionality will come easy.
This is pretty close:
// globals
var pairs = {
{ div : 'div1', url : 'http://some.net/address?client=Some+Client' } ,
{ div : 'div2', url : 'http://some.net/otheraddress?client=Some+Client' } ,
};
var since_record_id; //?? not sure what this is
var intervals = [];
// window onload
window.onload(){ // I don't think this is gonna work
for(var i; i<pairs.length; i++) {
getRecent(pairs[i]);
intervals.push(setInterval(function(){
pollForNew(map[i]);
}, 300000));
}
}
function getRecent(map){
var url = map.url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
function pollForNew(map){
var url = map.url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
and the html obviously needs two divs:
<div id='div1' class='data_div'></div>
<div id='div2' class='data_div'></div>
Your 'window.onload` - I don't think that's gonna work, but maybe you have it set up correctly and didn't want to bother putting in all the code.
About my suggested code - it defines an array in the global scope, an array of objects. Each object is a map, a dictionary if you like. These are the params for each div. It supplies the div id, and the url stub. If you have other params that vary according to div, put them in the map.
Then, call getRecent() once for each map object. Inside the function you can unwrap the map object and get at its parameters.
You also want to set up that interval within the loop, using the same parameterization. I myself would prefer to use setTimeout(), but that's just me.
You need to supply the loadResource() function that accepts a URL (string) and returns the HTML available at that URL.
This solves the problem of modularity, but it is not "an object" or class-based approach to the problem. I'm not sure why you'd want one with such a simple task. Here's a crack an an object that does what you want:
(function() {
var getRecent = function(url, div){
url = url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(div);
elt.innerHTML = content;
}
var pollForNew = function(url, div){
url = url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(div);
elt.innerHTML = content;
}
UpdatingDataDiv = function(map) {
if (! (this instanceof arguments.callee) ) {
var error = new Error("you must use new to instantiate this class");
error.source = "UpdatingDataDiv";
throw error;
}
this.url = map.url;
this.div = map.div;
this.interval = map.interval || 30000; // default 30s
var self = this;
getRecent(this.url, this.div);
this.intervalId = setInterval(function(){
pollForNew(self.url, self.div);
}, this.interval);
};
UpdatingDataDiv.prototype.cancel = function() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
})();
var d1= new UpdatingDataDiv('div1','http://some.net/address?client=Some+Client');
var d2= new UpdatingDataDiv('div2','http://some.net/otheraddress?client=Some+Client');
...
d1.cancel();
But there's not a lot you can do with d1 and d2. You can invoke cancel() to stop the updating. I guess you could add more functions to extend its capability.
OK, figured out what I needed. It's pretty straight forward.
First off disregard window.onload, the object is defined as a function and when you instantiate a new object it runs the function. Do your setup in the function.
Second, for global variables that you wish to make local to your object, simply define them as this.variable_name; within the object. Those variables are visible throughout the object, and its member functions.
Third, define your member functions as object.prototype.function = function(){};
Fourth, for my case, the object function should return this; This allows regular program flow to examine the variables of the object using dot notation.
This is the answer I was looking for. It takes my non-functional example code, and repackages it as an object...
function ServerObject(url){
// global to the object
this.server_url = url;
this.data = new Object();
this.since_record_id;
this.interval_id;
// do the onload functions
this.getRecent();
this.interval_id = setInterval(function(){
this.pollForNew();
}, 300000);
// do other stuff to setup the object
return this;
}
// define the getRecent function
ServerObject.prototype.getRecent = function(){
// do getRecent(); stuff
// reference object variables as this.variable;
}
// same for pollForNew();
ServerObject.prototype.pollForNew = function(){
// do pollForNew(); stuff here.
// reference object variables as this.variable;
}
Then in your program flow you do something like...
var server = new ServerObject("http://some.net/address");
server.variable = newValue; // access object variables
I mentioned the ADD in the first post. I'm smart enough to know how complex objects can be, and when I look for examples and explanations they expose certain layers of those complexities that cause my mind to just swim. It is difficult to drill down to the simple rules that get you started on the ground floor. What's the scope of 'this'? Sure I'll figure that out someday, but the simple truth is, you gotta reference 'this'.
Thanks
I wish I had more to offer.
Skip

Categories

Resources