├── .gitignore ├── 036-nbsp.md ├── assets ├── instruments-energy.png ├── split-view-example.png ├── console-time-example.png ├── composite-layers-safari.png └── Screen Shot 2016-09-23 at 16.23.21.png ├── 046-vim-ssh.md ├── 018-position-sticky.md ├── 007-ids.md ├── 039-debugging-print-stylesheets.md ├── 034-use-env-variables-in-shebang.md ├── 015-when-to-use-split-view.md ├── 014-lighttpd-response-compression.md ├── 029-run-optima-from-cli.md ├── 043-cursor-and-typing-speed-in-hyper.md ├── 044-safari-in-private-mode-and-local-storage.md ├── 032-rails-logrotate.md ├── 024-border-style.md ├── 020-performance.md ├── 023-rails-c-sandbox.md ├── 027-xcode-instruments-energy.md ├── 031-squish-and-nbsp.md ├── 033-use-sphinx-to-get-stop-words.md ├── 026-bunde-jobs.md ├── 037-gulp-changed-and-jade.md ├── 003-revert-git-push-force.md ├── 016-console.time.md ├── 022-fast-tap-in-safari-ios.md ├── README.md ├── 011-add-reference.md ├── 006-coffeescript-comprehensions.md ├── 028-composite-layers-in-inspector.md ├── 013-three-steps-to-using-es6-in-gulpfile.md ├── 005-nesting-in-bem.md ├── 041-non-scrollable-safari-iframe.md ├── 021-100vh-in-safari-ios.md ├── 040-upgrading-to-rails5-and-rubocop.md ├── 004-how-to-distinguish-between-block-and-elements.md ├── 012-invalid-characters-in-filenames.md ├── 008-relations-as-subqueries.md ├── 002-relation-merge.md ├── 009-shellescape.md ├── 038-change-errored-file-in-gulp-on-error.md ├── 025-css-initial.md ├── 019-requestIdleCallback.md ├── 042-safari-ios-10-missing-touchmove.md ├── 035-middleman-4-es6.md ├── 001-safe-constantize.md ├── 030-better-sphinx-excerpts.md ├── 017-40%-rule.md ├── 045-js-printable-char.md └── 010-awesome-page-transitions-in-react.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /036-nbsp.md: -------------------------------------------------------------------------------- 1 | ## Как поставить неразрывный пробел (` `) с клавиатуры 2 | 3 | `alt+space` 4 | -------------------------------------------------------------------------------- /assets/instruments-energy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vast/til/HEAD/assets/instruments-energy.png -------------------------------------------------------------------------------- /assets/split-view-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vast/til/HEAD/assets/split-view-example.png -------------------------------------------------------------------------------- /assets/console-time-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vast/til/HEAD/assets/console-time-example.png -------------------------------------------------------------------------------- /assets/composite-layers-safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vast/til/HEAD/assets/composite-layers-safari.png -------------------------------------------------------------------------------- /046-vim-ssh.md: -------------------------------------------------------------------------------- 1 | ## Как редактировать Вимом файлы по ССШ 2 | 3 | ```bash 4 | nvim scp://user@host//etc/resolv.conf 5 | ``` 6 | -------------------------------------------------------------------------------- /assets/Screen Shot 2016-09-23 at 16.23.21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vast/til/HEAD/assets/Screen Shot 2016-09-23 at 16.23.21.png -------------------------------------------------------------------------------- /018-position-sticky.md: -------------------------------------------------------------------------------- 1 | ## position: sticky 2 | 3 | Фаерфокс, Сафари и Сафаир в ИОС нативно умеют `position: sticky`. Для Хрома и Оперы — [полифилл](https://github.com/wilddeer/stickyfill). 4 | -------------------------------------------------------------------------------- /007-ids.md: -------------------------------------------------------------------------------- 1 | ## About `#ids` 2 | 3 | Instead of doing: 4 | 5 | ```ruby 6 | user_ids = users.pluck(:id) 7 | ``` 8 | 9 | Starting Rails 4.0.2 you can do: 10 | 11 | ```ruby 12 | user_ids = users.ids 13 | ``` -------------------------------------------------------------------------------- /039-debugging-print-stylesheets.md: -------------------------------------------------------------------------------- 1 | ## Как дебажить печатные стили 2 | 3 | Открываем веб-инспектор. Esc — Rendering — Emulate CSS Media — Print: 4 | 5 | 6 | -------------------------------------------------------------------------------- /034-use-env-variables-in-shebang.md: -------------------------------------------------------------------------------- 1 | ## Как задавать переменные окружения в шебанге 2 | 3 | ```javascript 4 | #!/usr/bin/env NODE_PATH=foo/app nodejs 5 | 6 | require('models/foo'); // require('foo/app/models/foo') 7 | ``` 8 | -------------------------------------------------------------------------------- /015-when-to-use-split-view.md: -------------------------------------------------------------------------------- 1 | ## Когда Split View полезен 2 | 3 | Удобно сравнивать производительность или данные двух версий страницы (например, боевой и локальной) в веб-инспекторе. 4 | 5 | 6 | -------------------------------------------------------------------------------- /014-lighttpd-response-compression.md: -------------------------------------------------------------------------------- 1 | ## Сжатие ответов в lighttpd 2 | 3 | В lighttpd mod_compress сжимает только статические файлы. Им нельзя сжать ответ от проксируемого сервера. В таком случае используйте mod_deflate (для 1.4 — патч, в 1.5 — из коробки). 4 | -------------------------------------------------------------------------------- /029-run-optima-from-cli.md: -------------------------------------------------------------------------------- 1 | ## Как запускать [Оптиму](http://getoptima.ru/) из командной строки 2 | 3 | Например, таким алиасом: 4 | 5 | ```bash 6 | alias optima="open $@ -a Optima" 7 | ``` 8 | 9 | И потом: 10 | 11 | ```bash 12 | $ optima README.md 13 | ``` 14 | -------------------------------------------------------------------------------- /043-cursor-and-typing-speed-in-hyper.md: -------------------------------------------------------------------------------- 1 | ## Как ускорить курсор и ввод в Hyper 2 | 3 | ``` 4 | defaults write co.zeit.hyper ApplePressAndHoldEnabled -bool false 5 | defaults write co.zeit.hyper InitialKeyRepeat -int 10 6 | defaults write co.zeit.hyper KeyRepeat -int 1 7 | ``` 8 | -------------------------------------------------------------------------------- /044-safari-in-private-mode-and-local-storage.md: -------------------------------------------------------------------------------- 1 | ## Как ведет себя Сафари в приватном режиме с localStorage 2 | 3 | 1. `localStorage.getItem()` возвращает `null`; 4 | 2. `localStorage.setItem()` выкидывает исключение `QuotaExceededError (DOM Exception 22): The quota has been exceeded`. 5 | -------------------------------------------------------------------------------- /032-rails-logrotate.md: -------------------------------------------------------------------------------- 1 | ## Как ротировать логи Рельсами 2 | 3 | Кабум! 4 | 5 | ```ruby 6 | # config/environments/staging.rb 7 | 8 | config.logger = ActiveSupport::TaggedLogging.new( 9 | Logger.new(config.paths["log"].first, 10, 50.megabytes) # 10 файлов по 50 Мб 10 | ) 11 | ``` 12 | -------------------------------------------------------------------------------- /024-border-style.md: -------------------------------------------------------------------------------- 1 | ## Значения по умолчанию у border-* 2 | 3 | Задание `border-style` тащит за собой значения для толщины и цвета рамки. 4 | 5 | ```css 6 | div { border-style: solid } 7 | ``` 8 | 9 | Хром и Сафари дадут диву [черную рамку в 3 пикселя](http://codepen.io/anon/pen/wMzoyO). 10 | -------------------------------------------------------------------------------- /020-performance.md: -------------------------------------------------------------------------------- 1 | ## performance.now() 2 | 3 | [`performance.now()`](https://developers.google.com/web/updates/2012/08/When-milliseconds-are-not-enough-performance-now) возвращает количество миллисекунд (с плавающей точкой), прошедших с момента начала загрузки страницы. В отличие от `Date.now()` не зависит от системного времени и перевода часов. 4 | -------------------------------------------------------------------------------- /023-rails-c-sandbox.md: -------------------------------------------------------------------------------- 1 | ## Режим песочницы у Рельсовой консоли 2 | 3 | `bin/rails c -s` запустит консоль в режиме песочницы. В конце сеанса Рельсы откатят все изменения, сделанные в ней: 4 | 5 | ``` 6 | bin/rails c -s 7 | Loading development environment in sandbox (Rails 4.2.4) 8 | Any modifications you make will be rolled back on exit 9 | ``` 10 | -------------------------------------------------------------------------------- /027-xcode-instruments-energy.md: -------------------------------------------------------------------------------- 1 | ## Отслеживание потребляемой энергии на айпаде 2 | 3 | Среди инструментов Икскода есть средства для мониторинга потребляемой энергии: Xcode — Instruments — Energy Diagnostics. 4 | 5 | Пригодится, чтобы отслеживать влияние изменений в коде на энергопотребление приложения: 6 | 7 | 8 | -------------------------------------------------------------------------------- /031-squish-and-nbsp.md: -------------------------------------------------------------------------------- 1 | ## Чем опасен `squish` 2 | 3 | `squish` выкидывает неразрывные пробелы из текста: 4 | 5 | ```ruby 6 | "дом 36"[3].ord # => 160, неразрывный пробел 7 | "дом 36".squish[3].ord # => 32, обычный пробел 8 | ``` 9 | 10 | Чтобы понять, почему это происходит, посмотрите [исходный код `squish!`](http://apidock.com/rails/String/squish!). 11 | -------------------------------------------------------------------------------- /033-use-sphinx-to-get-stop-words.md: -------------------------------------------------------------------------------- 1 | ## Как собрать стоп-слова самим Сфинксом 2 | 3 | `indexer` умеет выбирать самые частые слова в индексе: 4 | 5 | ```bash 6 | indexer --buildstops stopwords.txt 300 --config config/development.sphinx.conf your_index_core 7 | ``` 8 | 9 | В `stopwords.txt` окажутся 300 самых популярных слов, большая часть из которых — предлоги. 10 | -------------------------------------------------------------------------------- /026-bunde-jobs.md: -------------------------------------------------------------------------------- 1 | ## bundle install --jobs 2 | 3 | `bundle` умеет скачивать и устанавливать гемы параллельно: 4 | 5 | ```shell 6 | bundle install --jobs 4 7 | ``` 8 | 9 | Из [документации](http://bundler.io/v1.11/man/bundle-install.1.html): 10 | 11 | > --jobs=[<number>] 12 | > The maximum number of parallel download and install jobs. The default is 1. 13 | -------------------------------------------------------------------------------- /037-gulp-changed-and-jade.md: -------------------------------------------------------------------------------- 1 | ## Как правильно использовать gulp-changed с Jade/Pug 2 | 3 | Чтобы gulp-changed правильно отслеживал изменения в шаблонах, ему надо подсказать расширение: 4 | 5 | ```javascript 6 | return gulp.src(templates) 7 | .pipe($.changed('.build', { extension: '.html' })) 8 | .pipe($.jade()) 9 | .pipe(gulp.dest('.build')); 10 | ``` 11 | -------------------------------------------------------------------------------- /003-revert-git-push-force.md: -------------------------------------------------------------------------------- 1 | ## How to revert `git push --force` 2 | 3 | ``` 4 | $ git push --force origin master 5 | 6 | ... 7 | a1a1a1..b2b2b2 master -> master 8 | ``` 9 | 10 | Don't panic. Here `a1a1a1` is the latest commit in remote `master` before the push. So we can return to it by doing: 11 | 12 | ``` 13 | $ git reset --hard a1a1a1 14 | $ git push --force origin master 15 | ``` -------------------------------------------------------------------------------- /016-console.time.md: -------------------------------------------------------------------------------- 1 | ## console.time() 2 | 3 | `console.time` и `console.timeEnd` замеряют и выводят время в миллисекундах, потраченное на выполнение кода: 4 | 5 | ```javascript 6 | console.time('#calculateSpreads') 7 | spreads.forEach(function(spread) { 8 | spread.init(); 9 | }); 10 | console.timeEnd('#calculateSpreads') 11 | ``` 12 | 13 | 14 | -------------------------------------------------------------------------------- /022-fast-tap-in-safari-ios.md: -------------------------------------------------------------------------------- 1 | ## Быстрый тап в Сафари 2 | 3 | В Сафари ИОС [убирают задержку в 350мс у тапа](https://webkit.org/blog/5610/more-responsive-tapping-on-ios/). Быстрый тап включается в следующих ситуациях: 4 | 5 | * страница не зумится (user-scalable=no или minimum-scale равный maximum-scale); 6 | * `width=device-width` в начальном зуме; 7 | * в стилях у элемента `touch-action: manipulation`. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Сегодня я узнал 2 | 3 | Мы каждый день узнаем новое: как инлайнить SVG в CSS, как использовать ES2015 в Gulpfile, где работает position: sticky. Накапливать такие знания публично — полезно: растет коллективный опыт. Кроме того, так проще заметить застой в профессиональном росте: перестали поступать новые знания → отстой, деградация, работа риэлтором. 4 | 5 | СЯУшки — это заметки-зарубки о том, что нового я узнал. 6 | -------------------------------------------------------------------------------- /011-add-reference.md: -------------------------------------------------------------------------------- 1 | ## About `add_reference` in Rails migrations 2 | 3 | In Rails 4 there is `add_reference` helper for adding a reference column and its index and foreign key: 4 | 5 | ```ruby 6 | add_reference :products, :user, index: true, foreign_key: true 7 | ``` 8 | 9 | It's the same as: 10 | 11 | ```ruby 12 | add_column :products, :user_id 13 | add_index :products, :user_id 14 | add_foreign_key :products, :users, :user_id 15 | ``` -------------------------------------------------------------------------------- /006-coffeescript-comprehensions.md: -------------------------------------------------------------------------------- 1 | ## How to use comprehensions in CoffeeScript 2 | 3 | Take advantage of comprehensions whenever possible. Instead of: 4 | 5 | ```coffee 6 | results = [] 7 | 8 | for item in array 9 | results.push(item.name) 10 | ``` 11 | 12 | Use: 13 | 14 | ```coffee 15 | results = (item for item in array) 16 | ``` 17 | 18 | With filtering: 19 | 20 | ```coffee 21 | inputs = (input for input in inputs when input.type isnt "hidden") 22 | ``` -------------------------------------------------------------------------------- /028-composite-layers-in-inspector.md: -------------------------------------------------------------------------------- 1 | ## Композитные слои в Веб-инспекторе 2 | 3 | Композитный слой — поддерево ДОМ, отрисовываемое на ГПУ. Большое количество таких слоев становится проблемой на мобильных устройствах, из-за высокого потребления памяти. 4 | 5 | Чтобы обнаружить все композитные слои и проследить за их отрисовкой на айпаде, откройте Веб-инспектор. Затем выделите `` ноду — Layers — Child Layers: 6 | 7 | 8 | -------------------------------------------------------------------------------- /013-three-steps-to-using-es6-in-gulpfile.md: -------------------------------------------------------------------------------- 1 | # Three steps to start using ES6 in gulpfile 2 | 3 | 1. Make sure `babel` is set as a dependency in `package.json` 4 | 2. Rename `gulpfile.js` to `gulpfile.babel.js` 5 | 3. Start using ES6 6 | 7 | For live example see: 8 | 9 | * https://github.com/fatezh/fatezh/blob/7425122fe861f38dfb4fd3a58c4e0af570b171de/gulpfile.babel.js 10 | * https://github.com/fatezh/fatezh/tree/7425122fe861f38dfb4fd3a58c4e0af570b171de/tasks 11 | 12 | 13 | -------------------------------------------------------------------------------- /005-nesting-in-bem.md: -------------------------------------------------------------------------------- 1 | ## About nesting in BEM 2 | 3 | In BEM blocks should have plain structure. There is no need to repeat DOM nesting in BEM classes: 4 | 5 | ```css 6 | .nav__item__link { } 7 | ``` 8 | 9 | You won't be able to reuse this element in another context or refactor later. 10 | 11 | To reflect nesting it's enough to use only DOM: 12 | 13 | ```html 14 | 19 | ``` -------------------------------------------------------------------------------- /041-non-scrollable-safari-iframe.md: -------------------------------------------------------------------------------- 1 | ## Из-за чего может застревать ифрейм в Сафари 2 | 3 | В Сафари 9+ есть баг: в некоторых случаях при скроле тачбаром ифрейм прокручивается только вверх: 4 | 5 | * https://bugs.webkit.org/show_bug.cgi?id=150168 6 | * https://bugs.webkit.org/show_bug.cgi?id=169129 7 | 8 | В моем случае ифрейм не прокручивался из-за верхнего марджина у контейнера: 9 | 10 | ```html 11 | 12 |
13 | ``` 14 | -------------------------------------------------------------------------------- /021-100vh-in-safari-ios.md: -------------------------------------------------------------------------------- 1 | ## 100vh в Сафари ИОС 2 | 3 | В Сафари ИОС элемент с высотой в `100vh` имеет разную высоту, в зависимости от того, как Сафари посчитал высоту вьюпорта: с минимальным или полным тулбаром. 4 | 5 | Самое интересное — это [не баг, а фича](http://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html). 6 | 7 | > This is completely intentional. It took quite a bit of work on our part to achieve this effect. 8 | -------------------------------------------------------------------------------- /040-upgrading-to-rails5-and-rubocop.md: -------------------------------------------------------------------------------- 1 | ## Как Rubocop может помочь с апгрейдом до пятых Рельс 2 | 3 | Одна из проблем при апгрейде на пятые Рельсы — переход на keyword args params в тестах контроллеров: 4 | 5 | ```ruby 6 | # было 7 | get :show, id: post.id, format: :json 8 | 9 | # стало 10 | get :show, params: { id: post.id, format: :json } 11 | ``` 12 | 13 | Чтобы Рубокоп нашел все такие места и автоматически исправил их, укажите `TargetRailsVersion: 5.0` в `.rubocop.yml` и запустите `bin/rubocop -a`. 14 | -------------------------------------------------------------------------------- /004-how-to-distinguish-between-block-and-elements.md: -------------------------------------------------------------------------------- 1 | ## How to distinguish between blocks and elements in BEM 2 | 3 | When working with BEM or similar component-based CSS methodology it's up to you to decide whether an entity is a block or en element. Here are some principles that will help you to make such decision: 4 | 5 | * If you want to reuse an entity somewhere in a project without its parent — it's a block. 6 | * If entity doesn't make any sense without its parent (is impossible without its parent) — it's an element. -------------------------------------------------------------------------------- /012-invalid-characters-in-filenames.md: -------------------------------------------------------------------------------- 1 | ## About filenames 2 | 3 | There are some [characters that can't be used in filenames](https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words): 4 | 5 | ``` 6 | / \ ? * : | " < > 7 | ``` 8 | 9 | To accurately strip all of them use [Zaru](https://github.com/madrobby/zaru) or `parameterize`: 10 | 11 | ```ruby 12 | Zaru.sanitize! " what\ver//wird:user:nput:" 13 | # => "what erwirdusernput" 14 | " what\ver//wird:user:nput:".parameterize 15 | # => "what-er-wird-user-nput" 16 | ``` 17 | -------------------------------------------------------------------------------- /008-relations-as-subqueries.md: -------------------------------------------------------------------------------- 1 | ## About Using Relations as Subqueries 2 | 3 | Instead of using `pluck` + `where` combo which runs two queries: 4 | 5 | ```ruby 6 | project_ids = user.projects.pluck(:id) 7 | # SELECT id FROM projects WHERE ... 8 | Task.where(project_id: project_ids) 9 | # SELECT * FROM tasks WHERE project_id IN (....) 10 | ``` 11 | 12 | You can use relation directly and have only one query: 13 | 14 | ```ruby 15 | Task.where(project_id: user.projects) 16 | # SELECT * FROM tasks WHERE project_id IN (SELECT id FROM projects WHERE ...) 17 | ``` -------------------------------------------------------------------------------- /002-relation-merge.md: -------------------------------------------------------------------------------- 1 | ## About `Relation#merge` 2 | 3 | `#merge` allows you to merge AR relations. So instead of: 4 | 5 | ```ruby 6 | class Message < ActiveRecord::Base 7 | scope :unread, -> { where(read_at: nil) } 8 | end 9 | 10 | class Dialog < ActiveRecord::Base 11 | scope :with_unread_messages, 12 | -> { joins(:messages).where(messages: { read_at: nil }) } 13 | end 14 | ``` 15 | 16 | You could do: 17 | 18 | ```ruby 19 | class Dialog < ActiveRecord::Base 20 | scope :with_unread_messages, -> { joins(:messages).merge(Message.unread) } 21 | end 22 | ``` -------------------------------------------------------------------------------- /009-shellescape.md: -------------------------------------------------------------------------------- 1 | ### About `shellescape` 2 | 3 | When you need to execute some external program you'd usually use backticks or `%x()`: 4 | 5 | ```ruby 6 | %x(mplayer #{song.file}) 7 | ``` 8 | 9 | This leads you to "Command Injection" vulnerability. If `song.file` is `foo& sudo cat /etc/passwd` then Ruby will actually run: 10 | 11 | ```bash 12 | mplayer foo& sudo cat /etc/passwd 13 | ``` 14 | 15 | To properly escape shell input use `Shellwords.shellescape`: 16 | 17 | ```ruby 18 | %x(mplayer #{song.file.shellecape}) 19 | # runs mplayer foo\&\ sudo\ cat\ /etc/passwd 20 | ``` -------------------------------------------------------------------------------- /038-change-errored-file-in-gulp-on-error.md: -------------------------------------------------------------------------------- 1 | ## Как подменить сломанный .jade/.pug шаблон плейсхолдером 2 | 3 | ```javascript 4 | gulp.src('content/*.pug') 5 | .pipe($.pug({ basedir: '.' })) 6 | .on('error', function(err) { 7 | this.push(new File({ 8 | base: 'content/', 9 | path: err.path.replace(/\.jade/, '.html'), 10 | contents: Buffer.from('

Fucked Up

'), 11 | })); 12 | 13 | this.emit('end'); 14 | }) 15 | .pipe($.typograf({ lang: 'ru' })) // и типограф, и обработчики ниже получат плейсхолдер 16 | .pipe(gulp.dest('.build')); 17 | ``` 18 | -------------------------------------------------------------------------------- /025-css-initial.md: -------------------------------------------------------------------------------- 1 | ## initial в CSS 2 | 3 | Часто путаюсь со значениями по умолчанию для `max-height` (`none`, а не `auto`), `min-height` (`0`, а не `auto`) и `z-index` (`auto`, а не `0` или `none`). К счастью таких же беспамятных разработчиков как я, современные браузеры (кроме ИЕ) умеют ключевое слово `initial`: 4 | 5 | ```css 6 | max-height: initial; /* == none */ 7 | min-height: initial; /* == 0 */ 8 | z-index: initial; /* == auto */ 9 | ``` 10 | 11 | Подробнее [в спецификации](http://www.w3.org/TR/css-values/#common-keywords) и [caniuse.com](http://caniuse.com/#feat=css-initial-value). 12 | -------------------------------------------------------------------------------- /019-requestIdleCallback.md: -------------------------------------------------------------------------------- 1 | ## requestIdleCallback 2 | 3 | В 47-м Хроме у `requestAnimationFrame` появился напарник — функция `requestIdleCallback`. Браузер запустит переданный ей колбек в свободное от работы время. Как и когда использовать — читайте [Google Developers](https://developers.google.com/web/updates/2015/08/using-requestidlecallback). 4 | 5 | В остальных браузерах ее пока нет, но есть что-то похожее: 6 | 7 | * [https://gist.github.com/paullewis/55efe5d6f05434a96c36](https://gist.github.com/paullewis/55efe5d6f05434a96c36); 8 | * [https://github.com/aFarkas/requestIdleCallback](https://github.com/aFarkas/requestIdleCallback). 9 | -------------------------------------------------------------------------------- /042-safari-ios-10-missing-touchmove.md: -------------------------------------------------------------------------------- 1 | ## Как поправить неработающий preventDefault() на touchmove в Сафари под ИОС 10 2 | 3 | Если вы делаете драг-н-дроп, то важно не давать `touchmove` перемещать страницу на мобильных устройствах: 4 | 5 | ```js 6 | el.on('touchmove', (e) => { 7 | e.preventDefault(); 8 | // ... 9 | }); 10 | ``` 11 | 12 | В Сафари под ИОС 10 эта штука сломана: `touchmove` добирается до документа и вместе с элементом перетаскивается вся страница. 13 | Чтобы обойти эту проблему, добавьте пустой обработчик `touchmove` на `window`: 14 | 15 | ```js 16 | window.addEventListener('touchmove', () => {}); 17 | ``` 18 | -------------------------------------------------------------------------------- /035-middleman-4-es6.md: -------------------------------------------------------------------------------- 1 | ## Как использовать ES6/ES2015 в Middleman 4 2 | 3 | Middleman 4 работает с Sprockets 3.x, который давно умеет ES6. Устанавливаем `sprockets-es6` и 4 | просим `middleman-sprockets` обрабатывать и `.es6` файлы: 5 | 6 | ```ruby 7 | # Gemfile 8 | 9 | gem "middleman-sprockets", "~> 4.0.0.rc" 10 | gem "sprockets-es6" 11 | ``` 12 | 13 | ```ruby 14 | # config.rb 15 | 16 | require "sprockets/es6" 17 | activate :sprockets do |s| 18 | s.supported_output_extensions << ".es6" 19 | end 20 | ``` 21 | 22 | Если нужны полифилы: 23 | 24 | ```javascript 25 | // application.js.es6 26 | 27 | //= require babel/polyfill 28 | ``` 29 | -------------------------------------------------------------------------------- /001-safe-constantize.md: -------------------------------------------------------------------------------- 1 | ## About `#safe_constantize` 2 | 3 | The `String#constantize` method converts a string to the constant (class, module) that the string contains or throws a `NameError` if there is no such constant. 4 | 5 | ```ruby 6 | def category 7 | category_class.new(self) 8 | end 9 | 10 | def category_class 11 | "Category::#{target_type}".constantize 12 | rescue 13 | Category::Default 14 | end 15 | ``` 16 | 17 | Fortunately, there is `#safe_constantize` which returns `nil` if there is no such constant: 18 | 19 | ```ruby 20 | def category_class 21 | "Category::#{target_type}".safe_constantize || Category::Default 22 | end 23 | ``` 24 | -------------------------------------------------------------------------------- /030-better-sphinx-excerpts.md: -------------------------------------------------------------------------------- 1 | ## Как отдавать предложение целиком в выдержках Сфинкса 2 | 3 | По умолчанию Сфинкс «выкусывает» цитаты с найденными словами без разбора: 4 | 5 | > новостей, а при выделении частей текста не давайте словам внутри ссылок 6 | 7 | Чтобы получить предложение целиком: 8 | 9 | ```ruby 10 | Post.search search_query, 11 | excerpts: { 12 | passage_boundary: "sentence", # запрещаем цитаты разрывать предложение 13 | limit: 512, # и увеличиваем лимиты на количество слов и символов в результатах поиска 14 | around: 50 15 | } 16 | ``` 17 | 18 | Результат: 19 | 20 | > Сокращайте длинные заголовки новостей, а при выделении частей текста не давайте словам внутри ссылок переноситься на разные строки. 21 | -------------------------------------------------------------------------------- /017-40%-rule.md: -------------------------------------------------------------------------------- 1 | ## Правило 40% 2 | 3 | У «морских котиков» есть правило: когда ты думаешь, что больше не можешь, это 40% от того, что ты на самом деле можешь. 4 | 5 | Это же правило встречается в книге Эрика Бертрана Ларссена «Без жалости к себе»: 6 | 7 | > Офицер из школы военной авиации провел мелом на доске вертикальную черту. Внизу он написал 0, затем разметил шкалу вверх и пронумеровал деления в порядке увеличения. Максимум был обозначен десяткой. 8 | > 9 | > Он указал на 4 и сказал: «Вы думаете, что можете сделать столько». 10 | > 11 | > Затем ткнул пальцем в 2: «Ваша мама считает, что вы способны на столько». 12 | > 13 | > Он снова показал вверх, на цифру 7: «Мы, офицеры, знаем, что вы готовы на большее», — и пристально посмотрел на нас. 14 | > 15 | > «Реальность же такова, — палец остановился на 10. — Вы способны на то, о чем и подумать не можете». 16 | -------------------------------------------------------------------------------- /045-js-printable-char.md: -------------------------------------------------------------------------------- 1 | ## Как в JS определить, ввел ли пользователь печатный символ 2 | 3 | `keydown` и `keyup` возникают при нажатии любых клавиш, включая функциональные: Ctrl, Cmd, F1, ... 4 | `keypress` возникает при нажатии _символьной_ клавиши и помогает получить код-символа. 5 | 6 | К сожалению, `keypress` срабатывает при нажатии символьных клавиш, дающих непечатный символ: например, ctlr+g дает непечатный символ с кодом 7. 7 | 8 | Железобетонный способ проверить, печатный ли символ ввел пользователь, — пробник. Вставляем то, что пользователь ввел, в ДОМ и смотрим ширину элемента: 9 | 10 | ```js 11 | const .$probe = $(' { 16 | const char = String.fromCharCode(e.charCode) 17 | if (!char || !char.length) return 18 | 19 | if ($probe.html(char).width() > 0) { 20 | // handle printable char `char` 21 | } 22 | }) 23 | ``` 24 | -------------------------------------------------------------------------------- /010-awesome-page-transitions-in-react.md: -------------------------------------------------------------------------------- 1 | ## How to Animate Components Transition with React and React Router 2 | 3 | To animate routing transitions with React Router use `[CSSTransitionGroup](https://facebook.github.io/react/docs/animation.html)` component and a bit of CSS: 4 | 5 | ```jsx 6 | render() { 7 | const name = this.context.router.getCurrentPath() 8 | 9 | return ( 10 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | ``` 21 | 22 | ```css 23 | .application_container-enter::before { 24 | position: fixed; 25 | top: 0; 26 | left: 0; 27 | 28 | width: 1%; 29 | 30 | content: ""; 31 | transition: width .3s; 32 | 33 | border-bottom: 5px solid $text-color; 34 | } 35 | 36 | .application_container-enter.application_container-enter-active::before { 37 | width: 100%; 38 | } 39 | ``` 40 | 41 | 42 | 43 | --------------------------------------------------------------------------------