WinJS DOM-Loading - javascript

I've programmed an WinJS-App (Metro-App) for Windows 8. The problem is, that I get "nullpointer exception" as soon as I try to access a DOM(HTML)-Element like a div.
The problem code is this:
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function (element, options) {
document.getElementById("inhalt").innerHTML = "test"; // causes NullpointerException
}
});
But when I do this instead, there is no problem. But I don't want to wait 3 seconds each time.
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function (element, options) {
window.setTimeout(function() { document.getElementById("inhalt").innerHTML = "test"; }, 3000);
}
});
Why is NullPointerException thrown and how can I fix it?

This is probably because ready() is called before your page is parented into the DOM, so document.getElementById can't find it. You are being passed the root element in the ready function, so you can instead do:
element.querySelector('#inhalt').innerHTML = "test";
And that should work. It's a best practice for pages to not use ids inside of pages though, so just change it to a class class="inhalt" and make it element.querySelector('.inhalt').

Related

Javascript TypeError: Cannot read property when nested inside a window.onload function

SOLVED: Will move to close post but for future reference here is what happened. The children div in the parent div I was referencing were dependent on an AJAX call. So I set an interval to keep checking if the element existed with...
var checkExist = setInterval(function() {
if ($('parent').length) {
// do work on child elements
clearInterval(checkExist);
}
}, 100);
ORIGINAL POST BELOW
I am getting into writing some scripts to make testing tasks simple. I need to wait to run some of these until the page is fully loaded. Naturally I nest my code in ...
window.onload = function () { code in here }
So I am trying to store an element in a var that I know exists and I know works without being nested in onload. The var is written like this.
var element = document.getElementById('userPanel').getElementsByTagName('div')[0];
I can run this in console and it returns the element I need. When I run my script nested in the onload function. It throws a type error. Is there some fundamental principle of javascript I am overlooking here? Thanks!
For sake of clarity here is the whole code section glued together
window.onload = function () {
var element = document.getElementById('userPanel').getElementsByTagName('div')[0];
}
SOLVED: Will move to close post but for future reference here is what happened. The children div in the parent div I was referencing were dependent on an AJAX call. So I set an interval to keep checking if the element existed with...
var checkExist = setInterval(function() {
if ($('parent').length) {
// do work on child elements
clearInterval(checkExist);
}
}, 100);

Calling function in a callback before defining it in javascript?

I am having some trouble wrapping my head around this. I have a web application that is almost entirely built with javascript. It starts out with a basic template, then starts adding content to it as the user interacts. I am trying to use Greensock as the animation library which has the ability to use a progress slider to show how far you are in the animation, see the second box here: https://greensock.com/timelinemax
The issue is that it uses a callback onUpdate that is supposed to run that function on each frame. Then I can use it to make the slider track with the animation.
var mainTL = new TimelineLite({onUpdate:updateSlider});
function updateSlider() {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
This would work - except that the slider object doesn't exist yet. I don't know why, this is some of the last code to be included in the file, but I get a couple errors in the console log just loading the page `ReferenceError: sliderTimeline is not defined' but then everything works.
To try getting away from those errors, I tried to do it like this:
var mainTL = new TimelineLite({onUpdate:updateSlider});
$( document ).ready(function() {
function updateSlider() {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
});
except now it fails because the updateSlider' function hasn't been defined, and it fails to start at all. I could put them both in a$( document ).ready(function()`, but then they become local functions / variables and the 5 other javascript files I am working with don't have access to them.
Do I have to live with the errors, or is there something I am not thinking of?
You can check whether sliderTimeline exists before trying to call it. For example change function updateSlider() to:
function updateSlider() {
if (typeof sliderTimeline !== 'undefined') {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
}
Or if you know that sliderTimeline is declared, but not assigned yet:
function updateSlider() {
if (sliderTimeline) {
sliderTimeline.noUiSlider.set( mainTL.progress());
}
}
Note that this works because onUpdate is called frequently, so it will eventually be called when sliderTimeline is eventually defined.
Edit:
Additionally, you can assign global variables inside $( document ).ready() as long as you declare them outside of the function.
For example:
var mainTL;
var updateSlider;
$( document ).ready(function() {
updateSlider = function () {
sliderTimeline.noUiSlider.set( mainTL.progress());
};
mainTL = new TimelineLite({onUpdate: updateSlider});
});
If you look at their codepen page http://codepen.io/GreenSock/pen/FnsqC/ they have:
var tl = new TimelineMax({delay:0.5, repeat:3,
repeatDelay:2, onUpdate:updateStats,
onRepeat:updateReps, onComplete:restart});
function updateReps() {
reps++;
repeatCount.innerHTML = reps;
}
function updateStats() {
time.innerHTML = tl.time().toFixed(2);
totalTime.innerHTML = tl.totalTime().toFixed(2);
progress.innerHTML = tl.progress().toFixed(2);
totalProgress.innerHTML = tl.totalProgress().toFixed(2);
}
Meaning that you need to define the callback function of onUpdate.

How to attach an event listener to a javascript var

I am a somewhat green programmer, and quite new to javascript/jquery, but I thought I understood javascript events. Apparently not. I am not able to get event listeners to work as I'd like.
Given javascript:
var Thing = {
//stuff
update: function() {
$.event.trigger({type:'stateUpdate', more:stuff});
}
};
var Room = {
//more stuff
updateHandler: function (e) {
//handle event here
}
};
If I do jquery:
$(document).on('stateUpdate', $Room.updateHandler);
then it works fine, but I can't do either
$(Room).on('stateUpdate', $Room.updateHandler);
or
Room.addEventListerner('stateUpdate', $Room.updateHandler);
The first does nothing, the second gives .addEventListerner is not a function error.
I've googled for hours and can't figure it out. I found something that said .addEventListener only works on objects that implement EventListener, something about handleEvent, and something about functions automatically implementing EventListener. Nothing on how to make an object implement it. Is there no way to add listeners to javascript objects that aren't functions? Am I going to have to create an event handler object, or use 'document' or 'window' and have it call handlers? That seems really ugly.
Should the objects be functions in the first place? Would that work? It seems the current opinion is that making everything functions is just trying to make javascript into something it isn't.
AFAIK there are no way to add a event listener to the plain object, as it is not placed inside DOM. Events are firing inside DOM, and bubbling so your event listener for custom object won't receive it.
There is a http://www.bobjs.com/ framework that can help you implement custom events.
In response to #Barmar (sort of) I believe I worked this out. Confirmation on if this is a a good alternative or not would be nice, though. Basically, I have to do a subscriber thing, right? Almost event/listener, but not quite.
var thing = {
callbacks: {},
regCallback: function (key, which) {
callbacks[key] = which;
},
remCallback: function (key) {
callbacks[key].delete;
}
update: function(e) {
for(var i = 0, len = callbacks.length; i < len;i++){
callbacks[i](e);
};
}
};
var Room = {
updateHandler: function () {
//handle stuff
},
subscribe: function (which, callback) {
which.regCallback('room', callback);
}
unsub: function (which) {
which.remCallback('room');
}
};
//wherever/whenever I need to get updates something like
Room.subscribe(thing, Room.updateHandler);
//unsub
Room.unsub(thing);
Second error is caused by typo: addEventListerner has extra r in it.

What is the preferred pattern for re-binding jQuery-style UI interfaces after AJAX load?

This always gets me. After initializing all lovely UI elements on a web page, I load some content in (either into a modal or tabs for example) and the newly loaded content does not have the UI elements initialized. eg:
$('a.button').button(); // jquery ui button as an example
$('select').chosen(); // chosen ui as another example
$('#content').load('/uri'); // content is not styled :(
My current approach is to create a registry of elements that need binding:
var uiRegistry = {
registry: [],
push: function (func) { this.registry.push(func) },
apply: function (scope) {
$.each(uiRegistry.registry, function (i, func) {
func(scope);
});
}
};
uiRegistry.push(function (scope) {
$('a.button', scope).button();
$('select', scope).chosen();
});
uiRegistry.apply('body'); // content gets styled as per usual
$('#content').load('/uri', function () {
uiRegistry.apply($(this)); // content gets styled :)
});
I can't be the only person with this problem, so are there any better patterns for doing this?
My answer is basically the same as the one you outline, but I use jquery events to trigger the setup code. I call it the "moddom" event.
When I load the new content, I trigger my event on the parent:
parent.append(newcode).trigger('moddom');
In the widget, I look for that event:
$.on('moddom', function(ev) {
$(ev.target).find('.myselector')
})
This is oversimplified to illustrate the event method.
In reality, I wrap it in a function domInit, which takes a selector and a callback argument. It calls the callback whenever a new element that matches the selector is found - with a jquery element as the first argument.
So in my widget code, I can do this:
domInit('.myselector', function(myelement) {
myelement.css('color', 'blue');
})
domInit sets data on the element in question "domInit" which is a registry of the functions that have already been applied.
My full domInit function:
window.domInit = function(select, once, callback) {
var apply, done;
done = false;
apply = function() {
var applied, el;
el = $(this);
if (once && !done) {
done = true;
}
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
if (done) {
return;
}
$(ev.target).find(select).each(apply);
});
};
Now we just have to remember to trigger the 'moddom' event whenever we make dom changes.
You could simplify this if you don't need the "once" functionality, which is a pretty rare edge case. It calls the callback only once. For example if you are going to do something global when any element that matches is found - but it only needs to happen once. Simplified without done parameter:
window.domInit = function(select, callback) {
var apply;
apply = function() {
var applied, el;
el = $(this);
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
$(ev.target).find(select).each(apply);
});
};
It seems to me browsers should have a way to receive a callback when the dom changes, but I have never heard of such a thing.
best approach will be to wrap all the ui code in a function -even better a separate file -
and on ajax load just specify that function as a call back ..
here is a small example
let's say you have code that bind the text fields with class someclass-for-date to a date picker then your code would look like this ..
$('.someclass-for-date').datepicker();
here is what i think is best
function datepickerUi(){
$('.someclass-for-date').datepicker();
}
and here is what the load should look like
$('#content').load('/uri', function(){
datepickerUi();
})
or you can load it at the end of your html in script tag .. (but i dont like that , cuz it's harder to debug)
here is some tips
keep your code and css styles as clean as possible .. meaning that for text fields that should be date pickers give them one class all over your website ..
at this rate all of your code will be clean and easy to maintain ..
read more on OOCss this will clear what i mean.
mostly with jquery it's all about organization ... give it some thought and you will get what you want done with one line of code ..
edit
here is a js fiddle with something similar to your but i guess it's a bit cleaner click here

How to tell whether the $(window).load()/window.onload event has already fired?

I have a script that is being inserted dynamically via another script. The code in that script is wrapped inside the $(window).load() event because it requires the images on the page to have all loaded. In some browsers it works fine, but in others it seems not to fire because the page has already finished loading by the time the code is run.
Is there any way to check and see whether the page has already finished loading - either via jQuery or JavaScript? (including images)
In this situation, I don't have access to the onload event of the original document (aside from altering it via the loaded script - but that would seem to present the same problem).
Any ideas/solutions/advice would be greatly appreciated!
You could try setting up a handler that's invoked via a timeout that will check the images to see if their properties are available. Clear the timer in the load event handler so if the load event occurs first, the timer won't fire. If the properties aren't available, then the load event hasn't fired yet and you know that your handler will eventually be invoked. If they are, then you know that the load event occurred before your handler was set and you can simply proceed.
Pseudocode
var timer = null;
$(function() {
$(window).load( function() {
if (timer) {
clearTimeout(timer);
timer = null;
}
process();
});
timer = setTimeout( function() {
if (checkAvailable())
process();
}
}, 10*1000 ); // waits 10 seconds before checking
});
function checkAvailable()
{
var available = true;
$('img').each( function() {
try {
if (this.height == 0) {
available = false;
return false;
}
}
catch (e) {
available = false;
return false;
}
});
return available;
}
function process() {
... do the real work here
}
I wrote a plugin that may be of some use: http://plugins.jquery.com/project/window-loaded
I think your problem would resolve itself if you'd use $(document).ready instead of $(window).load - see the jquery documentation.
You guys should read this:
http://web.enavu.com/daily-tip/daily-tip-difference-between-document-ready-and-window-load-in-jquery/
Don't know if this is what you are after, but have you tried(?):
$(document).ready(function(){
...
});
http://docs.jquery.com/Events/ready#fn

Categories

Resources