Category Archives: javascript
В очередной раз ковыряясь с CSS-ужасом, задумывались ли вы о том, что всё это можно было бы сделать гораздо проще ? Простой пример. Есть div, занимающий некоторое пространство. Внутри него нужно разместить 2 div’a так, чтобы первый занимал по высоте столько, сколько нужно его содержимому, а второй – всё оставшееся пространство. Если вы полезли в гугл и стекофервлоу, могу вам сразу сказать – это невозможно. Да, вы не ослышались, нельзя никак этого добиться. Вы можете указывать абсолютное значение высоты первого div’a, можете использовать трюки с overflow:hidden (правда, почему-то советующие это забывают указать, что если ваше содержимое не влезет в блок, то вы его уже не увидите), можете пытаться что-то сделать с абсолютным позиционированием (тут скорее всего у вас отвалится скроллинг), можете играться с padding-top: -30000; margin-top: 30000. Но всегда будете сталкиваться с проблемами. Потому что по-нормальному это сделать вообще никак нельзя. В CSS3 появилась возможность определить размеры используя вычисления типа 100% – 25px. Но чтобы вычесть что-то, нужно это что-то знать, а узнать размер заголовка или другого блока можно только из джаваскрипта. То есть, в 21 веке в 2013 году HTML стал настолько крут, что в нём нельзя описать простейший случай размещения. Facepalm.
Ещё один пример (из недавнего). Есть div, плавающий слева. Есть плавающий справа. Тот, который слева, занимает чуть больше места по высоте, а правый выровнен с ним по верхней границе. А хочется, чтобы они были на одной линии снизу. Опять же, после получаса поисков по stackoverflow приходим к выводу, что этого тоже нельзя сделать! Ни vertical-align, ни различные комбинации вложенных блоков не спасают отца русской демократии. Самым лучшим способом, который работает, опять же становится явное задание line-height в пикселях. Я не знаю, чем думали проектировщики CSS и HTML, но это просто феерия абсурда. Расскажите об этом любому человеку, кто имел дело с WPF, и он будет смеяться гомерическим хохотом.
В общем, я тут подумываю над тем, как можно эту ситуацию исправить, и пока что не нашел более разумного решения, чем написание джаваскриптового движка, который бы поддерживал “Advanced” сценарии размещения, активизируясь при загрузке страницы. Сами правила задавались бы либо атрибутами DOM-элементов, либо как-то отдельно через js. Поискал в интернете что-то подобное, пока ничего не нашел. Как я понимаю, сейчас народ предпочитает продолжать обниматься с кактусом, в лучшем случае используя CSS-фреймворки с сеточной версткой. Но если вы случайно наткнётесь на что-то в этом роде, дайте знать.
За последние месяцы я частенько пользовался различными джаваскриптовыми контролами и плагинами к jQuery. Думаю, имеет смысл поделиться инфой о самых полезных из них. Пока список таков:
- jsTree
- Ace editor
- X-editable & Poshytip
Начнём с jsTree. Библиотека представляет собой плагин к jQuery. С помощью jsTree я делал дерево категорий. Категорий много, все сразу загружать проблематично. Поэтому в первую очередь я разобрался с тем, как настраивать аяксовую подгрузку json-данных. После этого самым важным было научиться определять выбранные элементы. Потом занялся улучшением UI – задал отдельную иконку для представления категорий с заданными правилами (это была админка для задания правил к отдельным категориям, соответственно, категории делились на те, для которых правил нет, и те, для которых они уже заданы). Научился открывать нужные ноды при загрузке дерева автоматически (при этом ajax-запрос на получение дочерних элементов формируется автоматически). Ну и напоследок включил отображение выделенного элемента а-ля OS X, с выделением всей строки полностью. Рассмотрим эти шаги подробнее. Возможно, опытным джаваскриптерам приведённые рецепты и пояснения покажутся чересчур банальными, но лично мне бы это очень помогло.
Хочу поделиться маленьким велосипедом. Частенько нам нужно загрузить динамически кусок html-разметки (например, какой-нибудь div) и вставить его в DOM страницы. Иногда этот кусок разметки содержит javascript в виде некоторых обработчиков событий. Но если загружать его в ajax запросе, то javascript выполнен не будет (и функции, которые вы определили, тоже не будут зарегистрированы). Для того, чтобы заставить это работать, нужно либо вынести весь js-код в отдельный js-файл и указать его на главной странице заблаговременно, либо ограничивать себя использованием только inline-javascript (это когда мы пишем <input type=”button” onclick=”someJsCode()”/>. В моем случае оба варианта были неудобны, а самым удобным был именно обычный javascript в загружаемых кусочках разметки (потому что IDE при редактировании этих частей лучше понимает контекст – не возникает варнингов о “неизвестных” функциях, айдишниках элементов, переменных итд”. Поэтому я сочинил функцию, которая принимает в качестве аргумента загруженную разметку, выдирает оттуда скрипты (содержимое тегов <script>), склеивает их в один элемент <script> и добавляет в document.body. После этого javascript-функции, определенные в загруженных скриптах регистрируются в пространстве имен, и нормально работают. Причем, если при следующем ajax-запросе необходимо будет поменять реализацию некоторых функций, ничего дополнительно делать не нужно – js-интерпретатор затрет старые функции (по имени) новыми.
Плюс к этому, так как код скриптов нам больше не нужен, и будет только мешаться при отладке, другая функция убирает все script-теги и возвращает разметку уже без скриптов. Её можно вставить в innerHTML элемента, куда мы хотели запихнуть динамический контент.
Код функции:
/** * Concatenates all javascript code in html into one big script and * creates appends script element with this code to document body. * @param html Markup with embedded scripts retrieved using ajax. * * TODO : mb additional work required for scripts with specified src */ function processEmbeddedScripts(html) { var tempDiv = document.createElement('div'); tempDiv.innerHTML = html; var scriptNodesList = tempDiv.getElementsByTagName('script'); if (scriptNodesList.length > 0) { var wholeScript = ''; for (var i = 0; i < scriptNodesList.length; i++) { var scriptCode = scriptNodesList.item(i); wholeScript = wholeScript + scriptCode.innerHTML + ' '; } var scriptElement = document.createElement('script'); scriptElement.type = 'text/javascript'; scriptElement.text = wholeScript; document.body.appendChild(scriptElement); } } /** * Removes all embedded scripts from specified piece of html markup * and returns the shrinked markup. */ function excludeEmbeddedScripts(html) { return html.replace(/<script(.|\s)*?\/script>/g, ''); } |
Использование:
// получаем html, очищенный от скриптов var htmlWithoutScripts = excludeEmbeddedScripts(rawHtml); // добавляем этот html в DOM document.getElementById('mydiv').innerHTML = htmlWithoutScripts; // и после того, как мы имеем уже полностью собранный DOM, достаем скрипты и регистрируем их // в документе. при этом скрипты будут выполнены браузером. processEmbeddedScripts(rawHtml); |
Возможно (см TODO), еще придется доработать этот велосипед для скриптов с указанным src (их нужно пропихивать в document.body без text, но с указанным src).
PS: При повторных выполнениях этого кода старые функции затираются новым кодом (это корректное поведение).
PS-2: В некоторых браузерах подобных подход тяжело отлаживать, поскольку добавленные таким образом скрипты браузер в инструментах разработчика либо не отображает полностью, либо не отображает до тех пор, пока не случится брейпоинт в этих кусочках кода. Поэтому приходится этот метод использовать редко, предпочитая классически выносить весь js-код в отдельные файлы.
2