jQuery code works in Chrome, not in IE9 - javascript

Pretty new to jQuery here, I've got a chunk of code that works OK in Chrome, but fails in IE9 (have not tried FF yet).
Here's the code:
var textColor = $('#navmenu-body').css('color');
textColor = textColor.slice(4);
In IE9, I get an error to the effect that slice can't be called because textColor is undefined.
I was not sure if it's because jQuery just can't find the #navmenu-body element or that it can't find the CSS attribute color.
So I did:
var j = $('#navmenu-body');
var textColor = $('#navmenu-body').css('color');
textColor = textColor.slice(4);
In IE9's console, j.length returns 0. So the selector is indeed, not working
Here's the #navmenu-body HTML DOM
<div id="navmenu-body" class="x-panel-body x-panel-body-cssmenu x-layout-fit x-panel-body-cssmenu" style="height: 398px; left: 0px; top: 0px; width: 200px;">
</div>
Do I need to do something else for IE9 support ?
As per T.J. Crowder answer, I think I may have a problem where the selector is called before the element I'm trying to get is available.
I am primarily developing in Chrome (using ExtJS which dynamically generates all HTML content, so there may be some conflicts with jQuery) and I had to change:
$(document).ready(function () {
...
var textColor = $('#navmenu-body').css('color');
textColor = textColor.slice(4);
});
to:
$(window).load(function () {
...
var textColor = $('#navmenu-body').css('color');
textColor = textColor.slice(4);
});
For it to work in Chrome. But that "fix" doesn't work in IE9 apparently. From what I can read, it would seem ExtJS (which generates all of my HTML content) is initialized after the DOM and jQuery is ready, so that would explain it... By why would it work in Chrome then ?
I think I need to open a new, ExtJS related question.

var j = $('#navmenu-body');
var textColor = $('#navmenu-body').css('color');
textColor = textColor.slice(4);
So in IE9's debugger, turns out j is defined so it would seem jQuery has troubles getting the CSS attribute and textColor ends up being null.
No. The way jQuery works, $(...) will always return a jQuery object. That jQuery object may have no matching elements in it, but it won't be undefined or null.
But that object is apparently empty, because that's the only time jQuery will return undefined from css("color"). (Even if the first element in the set has no color, it will return "", not undefined.)
Do I need to do something else for IE9 support ?
No, all current versions of jQuery support IE9 directly.
What's going on here is that in your IE9 testing, the selector #navmenu-body didn't match any elements as of when you ran it. You'll have to determine why that is, as we don't have enough information in the question to answer that.
At a guess, it sounds like IE9 is firing some event or callback earlier than Chrome is (or you have a race condition and it was just luck of the draw). Or you're using conditional comments and that element just doesn't exist in IE9. (IE9 still had conditional comments.) Or an error occurred elsewhere in IE9, stopping your code and preventing your adding that element to the DOM. Etc. So look at where you're doing this code relative to where that element is created. Fundamentally, if the element exists and is in the DOM as of when you run var textColor = $("#navmenu-body").css("color");, textColor will be a string. It may be an empty string, but it'll be a string.
Re your edit about switching from ready to load: It sounds like whatever ExtJS stuff you're doing to create the element is happening after an ajax call or some such, and that call is completing before the load event fires on Chrome (so the element is there) but after load fires on IE9 (so it isn't).
Ideally, you want to register a callback with ExtJS, telling ExtJS to call you when the operation creating the element is complete. Then you don't need to use ready or load.
A very dirty workaround is to poll:
$(document).ready(function () {
// ...
init();
function init() {
var navmenuBody = $('#navmenu-body');
if (!navmenuBody[0]) { // **Very** dirty workaround
setTimeout(init, 50); // It wasn't there yet, so check back in 50ms
return;
}
var textColor = navmenuBody.css('color');
textColor = textColor.slice(4);
}
});
But again, that's a very dirty workaround. I'm sure there's a relevant callback you can get from ExtJS.

Related

Javascript: Changing "document.body.onresize" does not take hold without "console.log"

I'm writing a website with a canvas in it. The website has a script that runs successfully on every refresh except for a line at the end. When the script ends with:
document.body.onresize = function() {viewport.resizeCanvas()}
"document.body.onresize" is unchanged. (I double-checked in Chrome's javascript console: Entering "document.body.onresize" returns "undefined".)
However, when the script ends with:
document.body.onresize = function() {viewport.resizeCanvas()}
console.log(document.body.onresize)
"document.body.onresize" does change. The function works exactly as it should.
I can't explain why these two functionally identical pieces of code have different results. Can anyone help?
Edit: As far as I can tell, "document.body" is referring to the correct "document.body". When I call console.log(document.body) just before I assign document.body.onresize, the correct HTML is printed.
Edit 2: A solution (sort of)
When I substituted "window" for "document" the viewport's "resizeCanvas" function was called without fail every time I resized the window.
Why does "window" work while "document" only works if you call "console.log" first? Not a clue.
Resize events: no go
Most browsers don't support resize events on anything other than the window object. According to this page, only Opera supported detecting resizing documents. You can use the test page to quickly test it in multiple browsers. Another source that mentions a resize event on the body element specifically also notes that it doesn't work. If we look at these bug reports for Internet Explorer, we find out that having a resize event fire on arbitrary elements was an Internet Explorer-only feature, since removed.
Object.observe: maybe in the future
A more general method of figuring out changes to properties has been proposed and will most likely be implemented cross-browser: Object.observe(). You can observe any property for changes and run a function when that happens. This way, you can observe the element and when any property changes, such as clientWidth or clientHeight, you will get notified. It currently works only in Chrome with the experimental Javascript flag turned on. Plus, it is buggy. I could only get Chrome to notify me about properties that were changed inside Javascript, not properties that were changed by the browser. Experimental stuff; may or may not work in the future.
Current solution
Currently, you will have to do dirty checking: assign the value of the property that you want to watch to a variable and then check whether it has changed every 100 ms. For example, if you have the following HTML on a page:
<span id="editableSpan" contentEditable>Change me!</span>
And this script:
window.onload = function() {
function watch(obj, prop, func) {
var oldVal = null;
setInterval(function() {
var newVal = obj[prop];
if(oldVal != newVal) {
var oldValArg = oldVal;
oldVal = newVal;
func(newVal, oldValArg);
}
}, 100);
}
var span = document.querySelector('#editableSpan');
// add a watch on the offsetWidth property of the span element
watch(span, "offsetWidth", function(newVal, oldVal) {
console.log("width changed", oldVal, newVal);
});
}
This works similarly to Object.observe and for example the watch function in the AngularJS framework. It's not perfect, because with many such checks you will have a lot of code running every 100 ms. Additionally any action will be delayed 100 ms. You could possibly improve on this by using requestAnimationFrame instead of setInterval. That way, an update will be noticed whenever the browser redraws your webpage.
What you can do is that if you know for certain what particular action triggers a resize on your element that doesn't resize the full window you can trigger a resize event so your browser recalculate all of the divs (if by the case the browser is not triggering the event correctly).
With Jquery:
$(window).trigger('resize');
In the other hand, if you have an action that resizes an element you can always hold from that action to handle other following logic.
<script>
function body_OnResize() {
alert('resize');
}
</script>
<body onresize="body_OnResize()"></body>

jQuery 2.0.3 bug - fadeIn(), show() broken in firefox - SecurityError: The operation is insecure

I have a very basic html element that I would like to fadeIn(). I am however using require.js so I believe this could be part of the problem. I am using jQuery 2.0.3 When using fadeIn I get this error:
SecurityError: The operation is insecure.
chrome://firebug/content/console/commandLineExposed.js
Line 5
I have never seen this before, I have reset firefox and my PC.
Html
<message-box>
<message-info></message-info>
<close-box>x</close-box>
</message-box>
JS
$('message-Box').fadeIn();
I only get this error with firefox v27. No other browsers are having this problem, but I haven't tested it in any older versions of FF
I am not seeking help for anything other than the error...
See the error in action? and run this command: SD.message.showMessage('Somehow this breaks everything', 'bad');
-----Edit-------
So sadly you'll need to test this Here I assure you this is SFW, its just the sign in page.
I am confident there must be something in my other JS files that is conflicting, but I, as yet, have not found the problem.
I removed a fiddle that was here as it in no way helped the question, since adding the bounty I want it to be as helpful as possible.
Second Edit
Oddly, when running any show(), hide(), fadeIn() etc an iframe is created at the base of the page, just before the body. I'll need to have a think in my code why this would be happening.
Third Edit
I have no reason or explanation for this, but updating to jQuery 2.1.0 has fixed my issues. If anybody can explain the problem then I'd love to give them the points :)
Stepping through the jQuery code, you eventually hit this internal function below. The security error is thrown when jQuery attempts to write to the iframe document. jQuery 2.1.0 has a different way of determining the default node display value so you can just treat this as a jQuery/browser combo bug. You can minimally recreate the security error by pasting the following into the console:
var iframe = jQuery("<iframe frameborder='0' width='0' height='0'/>").css( "cssText", "display:block !important" ).appendTo(document.documentElement);
iframe[0].contentWindow.document.write("<!doctype html><html><body>");
Internal jQuery function:
function css_defaultDisplay( nodeName ) {
var doc = document,
display = elemdisplay[ nodeName ];
if ( !display ) {
display = actualDisplay( nodeName, doc );
// If the simple way fails, read from inside an iframe
if ( display === "none" || !display ) {
// Use the already-created iframe if possible
iframe = ( iframe ||
jQuery("<iframe frameborder='0' width='0' height='0'/>")
.css( "cssText", "display:block !important" )
).appendTo( doc.documentElement );
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
doc.write("<!doctype html><html><body>");
doc.close();
display = actualDisplay( nodeName, doc );
iframe.detach();
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
}
return display;
}
As per specification custom elements shall have '-' in their tags, so your markup should look like this:
<message-box>
<x-message><div></div></x-message>
<x-close>x</x-close>
</message-box>
After the change and corresponding style updates it works as far as I can tell: http://jsfiddle.net/9Frn8/11/
Looks like this may be due to the absolute paths in your CSS file. I also see (in console) you are trying to do a call to localhost (which fails, of course). There seems to be some issues in your code that is causing Firefox to stop specific processes. Specifically, something that firefox considers cross-domain.
This is most likely a Same-Origin-Policy issue.

Firefox not rendering jQuery templates

I'm using jQuery templates for a website I developed and they work perfectly fine on Chrome, Safari, and even IE9, but the templates just won't render on Firefox. I'm loading them externally, and the $.get is processed (I've checked Firebug, the get goes through and pulls the right file), but then I'm greeted with a blank page and inspecting the html reveals the body element contains only the footer (included in the html), but with display : none, which is how it should be, so the javascript ran to completion (since the footer is hidden inside the loadtemplate functions). It seems that Firefox is simply skipping the $.tmpl() call. Here's the function :
var loadTemplate = function(templateName){
$.get(templateName, function(template){
$.tmpl(template).appendTo("body");
});
};
var loadHomePage = function(){
history = [];
clearPage();
loadTemplate("./templates/home.tmpl");
current_page = "./templates/home.tmpl";
}
var clearPage = function(){
$(".page-content").remove();
$(".page-header").remove();
$("#popup-container").remove();
$(".page-footer").hide();
};
Any help would be greatly appreciated, I can follow up with more code if required as well.
EDIT: even works on Opera, I don't need to support it so the styles don't work (using LESS which Opera doesn't support and a css file strictly for IE), but it still loads the templates without a problem.
Requesting a template may get processed as HTML by some browsers, which in-turn can result in a mangled template. Try setting the datatype to "text" so that all browsers properly return un-modified text.
$.get(templateName, function(template){
$.tmpl(template).appendTo("body");
}, "text");

Debug DOM mutations with Firebug before page load

I'm having problems with debugging DOM changes introduced by some JavaScript code I'm running. Somewhere in the code an element's class is changed, and I'm trying to pinpoint where exactly. Unfortunately, the new class name is so generic that searching through all the JS code gives too many results to be a viable option.
I've tried debugging a bit with Firebug, but despite the nice "Break on Attribute Change" feature, I can't get it to work in a way I would want. The Firebug demo works correctly, but it's a post load situation.
The problem seems to be that I want to watch for mutations before the page is fully loaded. I assume that the changes occur somewhere in $(document).ready(), so it's in the DOM, but I can't select elements for UI breakpoints as would be the case with the demo (after page load).
Is there some way to debug this kind of situation other than grepping/going through the code by hand?
I propose that you remove the target element where its classname is changed. With some luck, you may be able to generate an error in the JavaScript code, so you will find where is your problem.
Otherwise, if it's done by JQuery (addClass), you may want to modify the JQuery code itself just to figure out the callstack.
The code would look like this (make sure this code is the first code called after JQuery inclusion):
(function () {
var addClass = jQuery.fn.addClass;
jQuery.fn.addClass = function (value) {
for (var i = 0, l = this.length; i < l; i++) {
// Here you put your method to start the debugger if you get your right element
if (this[i].id === "abc") {
debugger;
}
}
addClass(value);
}
})();
Hope that helps.
This answer may sound pretty lame, but I honestly think the best solution for bugs like this is "deconstructing" your program. Just make a copy of the entire project, then rip it apart. Take out chunks of code one by one. Turn function calls into stub functions or whatever to keep things running. Find the minimal amount of code that triggers the bug. Then the solution should be obvious.
Have you considered adding a mutation event? The event I think you want, DOMAttrModified, is not supported in webkit, so you might have to test with Firefox or Opera. In fact it is deprecated in DOM level 3.
There are two jQuery plugins for mutation events here (documentation) and here but since you want to do this before page load they might not be the answer.
You are probably best writing your own JavaScript bind for this event - there is an example in the answer to is there an alternative to DOMAttrModified that will work in webkit
I hope this helps.
If you want to use the "Break on Attribute Change" feature to debug, you can do the following:
Comment out all JS in the page (which is hopefully all in the head) except for the base jQuery load.
Load the page and set your watch points.
Add a button, which fires the JS, to the HTML. Or, optionally fire it from the console.
Trigger the JS load/fire. Hopefully your watch and break points will fire as desired.
For example, suppose your page loads/has:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script>
<script src="/Library_X.js" type="text/javascript"></script>
<script src="/MyJS.js" type="text/javascript"></script>
Then you could run this code in the console after setting the watch points:
function addJS_Node (text, s_URL)
{
var scriptNode = document.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
document.head.appendChild (scriptNode);
}
addJS_Node (null, '/Library_X.js');
//-- Possible pause here.
addJS_Node (null, '/MyJS.js');
// etc.
Or temporarily code a button that fires the same JS, into the page's HTML.

Setting Focus to an iFrame in IE

There is a tree menu in my application and on click of the menu items, it loads a url in a iFrame. I like to set the focus in an element of the page loaded in the iFrame.
I'm using this code, and it works perfectly in all the browsers except IE:
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').focus();
I have tried all different options like using setTimeout, but no chance.
After the page loads, when I hit the tab key, it goes to the second input, which means it's been on the first input, but it doesn't show the cursor!
I am using ExtJS and the ManagedIFrame plugin.
Any help is appreciated.
You need to call the focus() method of the iframe's window object, not the iframe element. I'm no expert in either jQuery or ExtJS, so my example below uses neither.
function focusIframe(iframeEl) {
if (iframeEl.contentWindow) {
iframeEl.contentWindow.focus();
} else if (iframeEl.contentDocument && iframeEl.contentDocument.documentElement) {
// For old versions of Safari
iframeEl.contentDocument.documentElement.focus();
}
}
Is the iFrame visible onload, or shown later? The elements are created in different order which is the basis of the setTimeout approach. Did you try a high value wait time on a set timeout?
Try something like at least a half second to test...IE tends to do things in a different order, so a high timeout may be needed to get it not to fire until render/paint finishes:
$(function(){
setTimeout(function() {
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').focus();
}, 500);
});
Difficult to troubleshoot without a working example, but you might try hiding and showing the input as well, to force IE to redraw the element.
Something like:
var myIFrame = $("#iframeName");
myIFrame.focus();
myIFrame.contents().find('#inputName').hide();
var x = 1;
myIFrame.contents().find('#inputName').show().focus();
This might jolt IE into displaying the cursor.
I could get IE to focus an input field in an iframe with:
iframe.focus();
var input = iframe...
input.focus();
iframe.contentWindow.document.body.focus();
input.focus();

Categories

Resources