IE innerHTML CSS bug
Today I encountered a bug that affects every version of Internet Explorer from IE6 all the way through IE9. Here’s what happens.
The Bug
- Set a Javascript string variable containing CSS styles (
<style>or<link>) followed by some HTML content, such as a<p>tag (in my application this happened via JSONP). - Add a
<div>node to the DOM - Insert the content from #1 into the
.innerHTMLproperty of the<div> - The content gets inserted, devoid of CSS styles.
<!-- these styles are ignored when inserted via innerHTML -->
<style>
p { color: blue; }
</style>
<p>Hello, how are you!</p>
The Solution
The simple solution was to put the other markup before the <style> or <link> tag. Bizarre.
<p>Hello, how are you!</p>
<!-- these styles are applied when inserted via innerHTML -->
<style>
p { color: blue; }
</style>
Because IE needs something that affects the layout before it will apply CSS styles in an innerHTML property, these will not work as style triggers:
<!-- a comment --><p></p>(or any other empty block-level HTML tag)
Building MySQL web applications with a team of developers will inevitably present the challenge of database schema changes. Any good web developer understands the importance of keeping all code under version control, but how many follow the same principle for the database?
What follows are some best practices that I have learned over the past several years from the school of hard knocks. read more…
Sometimes you want to do some sort of special processing in Javascript on mobile browsers, or vice versa. Here’s a quickie Javascript way to do that:
function is_mobile() {
var agents = ['android', 'webos', 'iphone', 'ipad', 'blackberry'];
for(i in agents) {
if(navigator.userAgent.match('/'+agents[i]+'/i')) {
return true;
}
}
return false;
}
This version is fast and easy to extend to detect additional browsers by changing the agents array. Here’s a much more thorough version, modified from detectmobilebrowsers.com:
function is_mobile() {
return /android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4));
}
Since this is a single gigantic regex, I suspect that it would run more slowly than the previous version, and is also more challenging to modify.
Instead of browser user-agent sniffing, you might want to consider using browser feature detection instead to tailor your application to the capabilities of the browser, rather than relying on the userAgent string (which can be customized in most browsers).
Enjoy!
After three recent virus infections on Windows XP and Windows 7 (including at least one rootkit infection), I turned to Ubuntu Linux as a safer operating system. Two of the PCs were blessed with Atheros-based wireless network adapters, which are well-supported on Linux. The other laptop, a Dell Inspiron 2200, is blessed with one of those infamous Broadcom chipsets.
Supposedly, the BCM4328 (rev 02) wireless chipset is supported on Linux, but as of Ubuntu Desktop 11.04 and Linux Mint 11, it doesn’t work reliably. So I turned to the old tried-and-true ndiswrapper to run the BCM4328 Windows driver under Linux. read more…
http_build_query(): surprise!
Did you know that PHP’s http_build_query() drops any key from your query array if the value is NULL? I wonder how many subtle API client bugs are caused by this behavior.
The workaround is easy, just set values that should be intentionally empty to boolean FALSE, integer 0, or an empty string.
If you have ever touched a MySQL slave, you know that they can and do frequently halt. While sync problems can be caused by many things—network outages, schema changes, etc—one of the most common problems in a dual-master setup is primary key collision.
Primary Key Collision
…happens when records are added on two different servers to the same table and get the same AUTO_INCREMENT value. Fortunately, there is a trivially easy way to prevent this from happening.
auto-increment-increment=N
Adding this to your my.cnf or my.ini file will make AUTO_INCREMENT increment by N rather than by 1. N is the number of replicated servers that are masters.
Combine this setting with…
auto-increment-offset=N
…to ensure that each replicated master uses unique AUTO_INCREMENT values. N should match the server-id setting.
With these two settings in place, your primary keys will never collide. read more…
Phirehose is an awesomely useful Twitter Streaming API client library, written in PHP by Fenn Bailey.
Heartbeat logging is something that I originally added for Rainmaker, and I finally got around to contributing those modifications, which you can see here on GitHub.
Why log heartbeats in Phirehose?
- To gain assurance that Phirehose is still alive, and actually functioning. In our case, missing tweets means lost money and unhappy clients. We needed to monitor this very closely.
- To enable automatically detecting connection drops and rewinding the count parameter to pick up those tweets, or backfilling them in using the Twitter Search API.
- To collect usage data for reporting purposes.
Usage
To use this, simply declare a heartbeat(array $data) method in your Phirehose child class. read more…
When uploading a file to a PHP script on an Apache web server, there are several configuration options that if improperly set can get in the way. I just encountered yet another one of these, and decided to catalog them here.
Size, Time, and Memory
There are three types of limits that affect file uploads, and the weakest link in the chain is your effective limit.
If your size limit is set to 3gb, but your time limit does not allow for the time required to upload that much data, you’ll still be unable to upload those large files. Likewise, the ability to upload does no good if you do not have enough memory to process the file that was uploaded.
Assumptions
This post assumes an 8MB upload limit (8mb x 1024kb x 1024 bytes = 8388608). You will want to adjust this number up or down according to your needs.
Oddly, although 8mb is the default value for PHP’s upload_max_filesize setting, some of the other default settings are much lower (2mb, or in some cases, only 100k).
PHP limits
upload_max_filesize = 8388608
post_max_size = 8388608
max_input_time = 60
Depending on what you’re doing with the uploaded files, you may also need to increase your memory limit:
memory_limit = 64MB
Apache limits
If LimitRequestBody is set to something non-zero, you may need to increase its value in your Apache httpd.conf file or .htaccess file:
LimitRequestBody 8388608
If you are using mod_fcgid (required to run the latest PHP 5.3 VC9 NTS build for Windows), then you need to set the value of FcgidMaxRequestLen, which defaults to 100k if it is not set. (Note that some systems may put mod_fcgid settings in a file separate from the main httpd.conf file).
FcgidMaxRequestLen 8388608
FcgidIOTimeout 60
Happy uploading!
A plugged-in-and-giving-back mentality
While working on the Chrome version of a browser extension for a client of my company, I subscribed to the chromium-extensions Google group. I did this both for my own edification as well as for others’ benefit.
Every now and then, a message comes through that relates to my personal experience or knowledge which has not yet been answered, and I will answer it.
When you contribute to an open-source community, it does two things: it builds your reputation within the community, and it increases the likelihood of getting quality help yourself when you need it. There’s also the bonus of promoting your company if you use an email signature. This has SEO value since most of the time these messages wind up in search engines.
Besides chromium-extensions, I am also subscribed to phirehose-users (my participation here earned commit access as an official contributor), twitter-development-talk, and some local PHP user groups lists.
I wish this kind of plugged-in-and-giving-back mentality were more prevalent. If you use an open-source project or technology, subscribe to the mailing list (or forum), and don’t just lurk. Answer questions, and where appropriate, share your code or your experiences.
