├── .gitignore ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE.md ├── README.md ├── dist ├── react-input-placeholder.js └── react-input-placeholder.min.js ├── package.json └── src ├── react-input-placeholder.js └── umd.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.DS_Store 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.2.0 2 | ### Bugfixes 3 | - [Fix prop warning](https://github.com/enigma-io/react-input-placeholder/pull/12) 4 | - [Remove createFactory calls (fixes JSX usage)](https://github.com/enigma-io/react-input-placeholder/pull/11) 5 | - [Fix IE8 incompatibility](https://github.com/enigma-io/react-input-placeholder/pull/4) 6 | 7 | **If you are not using JSX, you will need to wrap the use of this module in `createFactory`.** 8 | 9 | ## 1.1.0 10 | ### Bugfixes 11 | - [Make module compatible with newer versions of React](https://github.com/enigma-io/react-input-placeholder/pull/7) 12 | - [Check for document before using it](https://github.com/enigma-io/react-input-placeholder/pull/6) 13 | 14 | ### Deprecations 15 | - Support for React versions `< 0.13.0` will be removed in `2.0.0` 16 | 17 | ## 1.0.0 18 | - Initial commit 19 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | browserify: { 5 | dist: { 6 | files: { 7 | 'dist/react-input-placeholder.js': ['src/umd.js'] 8 | }, 9 | } 10 | }, 11 | uglify: { 12 | options: { 13 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>) - <%= pkg.author %> */\n' 14 | }, 15 | dist: { 16 | files: { 17 | 'dist/react-input-placeholder.min.js': ['dist/react-input-placeholder.js'] 18 | } 19 | } 20 | }, 21 | jshint: { 22 | options: { 23 | camelcase: true, 24 | nonew: true, 25 | plusplus: true, 26 | quotmark: true, 27 | bitwise: true, 28 | forin: true, 29 | curly: true, 30 | eqeqeq: true, 31 | immed: true, 32 | latedef: true, 33 | newcap: true, 34 | noarg: true, 35 | undef: true, 36 | unused: true, 37 | regexp: true, 38 | trailing: true, 39 | node: true, 40 | browser: true, 41 | laxbreak: true 42 | }, 43 | gruntfile: { 44 | files: { 45 | src: ['Gruntfile.js'] 46 | } 47 | }, 48 | dev: { 49 | files: { 50 | src: ['src/**/*.js'] 51 | }, 52 | options: { 53 | debug: true, 54 | devel: true 55 | } 56 | }, 57 | dist: { 58 | files: { 59 | src: ['src/**/*.js'] 60 | } 61 | } 62 | } 63 | }); 64 | 65 | grunt.loadNpmTasks('grunt-contrib-jshint'); 66 | grunt.loadNpmTasks('grunt-browserify'); 67 | grunt.loadNpmTasks('grunt-contrib-uglify'); 68 | 69 | grunt.registerTask('lint', ['jshint']); 70 | grunt.registerTask('dist', ['browserify', 'uglify']); 71 | grunt.registerTask('default', ['jshint', 'dist']); 72 | }; 73 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Enigma Technologies, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Input and Textarea with Placeholder Shim 2 | ======================= 3 | 4 | `PlaceholderShim` provides `Input` and `Textarea`, small wrappers around `React.createElement('input')` and `React.createElement('textarea')` respectively that shims in `placeholder` functionality for browsers that don't natively support it. Currently only tested with IE9. 5 | 6 | Demo: https://jsfiddle.net/69z2wepo/16498/ 7 | 8 | ## Getting Started 9 | ### Browserify 10 | 11 | Install: `npm install react-input-placeholder` 12 | 13 | Require: 14 | 15 | ```js 16 | Input = require('react-input-placeholder')(React).Input; 17 | Textarea = require('react-input-placeholder')(React).Textarea; 18 | ``` 19 | 20 | ### No module 21 | 22 | The compiled bundle sits in the `dist/` folder. 23 | 24 | ```js 25 | 26 | 30 | ``` 31 | 32 | ## Usage 33 | 34 | You can use `Input` or `Textarea` exactly the same way you'd use `React.createElement('input')` or `` in JSX. All attributes will be passed on, and all event callbacks will be called. However, please note that the placeholder shim only works on [controlled](http://facebook.github.io/react/docs/forms.html#controlled-components) inputs (i.e., you must provide a `value` or `valueLink` prop). 35 | 36 | When the placeholder text is visible, the `placeholder` CSS class will be added to the `input` element so you can style it, e.g. 37 | 38 | ```css 39 | input.placeholder, 40 | textarea.placeholder { 41 | color: gray; 42 | font-style: italic; 43 | } 44 | ``` 45 | 46 | ### Before 47 | 48 | Placeholder doesn't show on IE9. 49 | 50 | ```html 51 | 52 | ``` 53 | 54 | ### After 55 | 56 | Works on IE9! 57 | 58 | ```html 59 | 60 | ``` 61 | 62 | ## Building yourself 63 | 64 | ```shell 65 | npm install 66 | grunt dist 67 | ``` 68 | 69 | ## License 70 | 71 | MIT 72 | -------------------------------------------------------------------------------- /dist/react-input-placeholder.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o (http://enigma.io) */ 2 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g (http://enigma.io)", 6 | "main": "src/react-input-placeholder.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/enigma-io/react-input-placeholder.git" 10 | }, 11 | "keywords": [ 12 | "placeholder", 13 | "input", 14 | "react", 15 | "react-component" 16 | ], 17 | "contributors": [ 18 | "Joe Natalzia", 19 | "Evan Jacobs " 20 | ], 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/enigma-io/react-input-placeholder/issues" 24 | }, 25 | "browser": { 26 | "react": "react" 27 | }, 28 | "devDependencies": { 29 | "grunt": "^0.4.1", 30 | "grunt-browserify": "^1.2.9", 31 | "grunt-contrib-jshint": "^0.7.0", 32 | "grunt-contrib-uglify": "^0.2.4", 33 | "react": "^0.13.3" 34 | }, 35 | "scripts": { 36 | "build": "grunt", 37 | "test": "grunt lint" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/react-input-placeholder.js: -------------------------------------------------------------------------------- 1 | var isPlaceholderSupported = (typeof document !== 'undefined') 2 | && 'placeholder' in document.createElement('input'); 3 | 4 | /** 5 | * Input is a wrapper around React.DOM.input with a `placeholder` shim for IE9. 6 | * NOTE: only supports "controlled" inputs (http://facebook.github.io/react/docs/forms.html#controlled-components) 7 | */ 8 | var createShimmedElement = function(React, elementConstructor, name) { 9 | return React.createClass({ 10 | displayName: name, 11 | 12 | componentWillMount: function() { 13 | this.needsPlaceholding = this.props.placeholder && !isPlaceholderSupported; 14 | }, 15 | 16 | componentWillReceiveProps: function(props) { 17 | this.needsPlaceholding = props.placeholder && !isPlaceholderSupported; 18 | }, 19 | 20 | // this component supports valueLink or value/onChange. 21 | // borrowed from LinkedValueMixin.js 22 | getValue: function() { 23 | if (this.props.valueLink) { 24 | return this.props.valueLink.value; 25 | } 26 | 27 | return this.props.value; 28 | }, 29 | 30 | getOnChange: function() { 31 | if (this.props.valueLink) { 32 | return this._handleLinkedValueChange; 33 | } 34 | 35 | return this.props.onChange; 36 | }, 37 | 38 | _handleLinkedValueChange: function(e) { 39 | this.props.valueLink.requestChange(e.target.value); 40 | }, 41 | 42 | // keep track of focus 43 | onFocus: function(e) { 44 | this.hasFocus = true; 45 | this.setSelectionIfNeeded(e.target); 46 | 47 | if (this.props.onFocus) { 48 | return this.props.onFocus(e); 49 | } 50 | }, 51 | onBlur: function(e) { 52 | this.hasFocus = false; 53 | 54 | if (this.props.onBlur) { 55 | return this.props.onBlur(e); 56 | } 57 | }, 58 | 59 | setSelectionIfNeeded: function(node) { 60 | if ( this.needsPlaceholding 61 | && 'setSelectionRange' in node 62 | && this.hasFocus 63 | && this.isPlaceholding 64 | && (node.selectionStart !== 0 || node.selectionEnd !== 0)) { 65 | node.setSelectionRange(0, 0); 66 | } // if placeholder is visible, ensure cursor is at start of input 67 | }, 68 | 69 | onChange: function(e) { 70 | var onChange = this.getOnChange(); 71 | var value; 72 | var index; 73 | 74 | if (this.isPlaceholding) { 75 | // remove placeholder when text is added 76 | value = e.target.value; 77 | index = value.indexOf(this.props.placeholder); 78 | 79 | if (index !== -1) { 80 | e.target.value = value.slice(0, index); 81 | } 82 | } 83 | 84 | if (onChange) { 85 | return onChange(e); 86 | } 87 | }, 88 | 89 | onSelect: function(e) { 90 | if (this.isPlaceholding) { 91 | this.setSelectionIfNeeded(e.target); 92 | 93 | return false; 94 | } else if (this.props.onSelect) { 95 | return this.props.onSelect(e); 96 | } 97 | }, 98 | 99 | componentDidUpdate: function() { 100 | this.setSelectionIfNeeded(this.getDOMNode()); 101 | }, 102 | 103 | render: function() { 104 | var props = {}; 105 | var value; 106 | var key; 107 | 108 | for (key in this.props) { 109 | if (this.props.hasOwnProperty(key)) { 110 | props[key] = this.props[key]; 111 | } 112 | } 113 | 114 | if (this.needsPlaceholding) { 115 | // override valueLink and event handlers 116 | props.onFocus = this.onFocus; 117 | props.onBlur = this.onBlur; 118 | props.onChange = this.onChange; 119 | props.onSelect = this.onSelect; 120 | props.valueLink = undefined; 121 | 122 | value = this.getValue(); 123 | 124 | if (!value) { 125 | this.isPlaceholding = true; 126 | value = this.props.placeholder; 127 | props.className += ' placeholder'; 128 | } else { 129 | this.isPlaceholding = false; 130 | } 131 | 132 | props.value = value; 133 | } 134 | 135 | if (!('createElement' in React)) { /* start -- to be removed in 2.0.0 */ 136 | return this.transferPropsTo(elementConstructor()); 137 | } else { /* -- end */ 138 | return React.createElement(elementConstructor, props, this.props.children); 139 | } 140 | } 141 | }); 142 | }; 143 | 144 | module.exports = function(React) { 145 | if (!('createElement' in React)) { /* start -- to be removed in 2.0.0 */ 146 | return { 147 | Input: createShimmedElement(React, React.DOM.input, 'Input'), 148 | Textarea: createShimmedElement(React, React.DOM.textarea, 'Textarea') 149 | }; 150 | } else { /* -- end */ 151 | return { 152 | Input: createShimmedElement(React, 'input', 'Input'), 153 | Textarea: createShimmedElement(React, 'textarea', 'Textarea') 154 | }; 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /src/umd.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | var reactInputPlaceholder = require('./react-input-placeholder'); 3 | if (typeof define === 'function' && define.amd) { 4 | define(['react'], function (React) { 5 | return reactInputPlaceholder(React); 6 | }); 7 | } else { 8 | window.PlaceholderShim = reactInputPlaceholder(window.React); 9 | } --------------------------------------------------------------------------------