Хобрук: Ваш путь к мастерству в программировании

Создание узла стилей, добавление innerHTML, добавление в DOM и головные боли IE

У меня вопрос из двух частей.

Во-первых, сценарий:

Из-за некоторых странных проблем, с которыми мы столкнулись в связи с поддержкой NOSCRIPT мобильным браузером, мне поручено придумать альтернативное решение для «обнаружения» JS. Логика решения состоит в том, чтобы на странице было два DIV. Одна из них - ошибка, указывающая, что у вас нет JS, и он отображается по умолчанию. Если у кого-то есть JS, мы хотим добавить новый блок STYLE в HEAD, который заменяет предыдущий CSS и скрывает ошибку, а вместо этого показывает содержимое.

Пример HTML:

<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>

Это JS, с которого я начал:

  var styleNode = document.createElement('style');
  styleNode.setAttribute("type", "text/css");
  styleNode.innerHTML = "#div1 {display: block;} #div2 {display: none;}";
  headTag.appendChild(styleNode);

Но у меня были проблемы. Некоторый поиск в Google приводит к такому описанию проблемы безопасности, которая может возникнуть в IE, если вы попытаетесь вставить innerHTML в созданный элемент перед его размещением в DOM:

http://karma.nucleuscms.org/item/101

Итак, я модифицировал скрипт как таковой:

  var styleNode = document.createElement('style');
  styleNode.setAttribute("type", "text/css");
  var headTag = document.getElementsByTagName("head")[0];
  headTag.appendChild(styleNode);
  var aStyleTags = headTag.getElementsByTagName("style");
  var justAddedStyleTag = aStyleTags[aStyleTags.length-1];
  justAddedStyleTag.innerHTML = "#div1 {display: block;} #div2 {display: none;}";

вопрос 1: это допустимый способ решения проблемы IE? Есть ли более эффективное решение?

вопрос 2: даже после настройки скрипт все равно не работает в IE. Он отлично работает в Firefox, но в IE 7 я получаю «неизвестную ошибку времени выполнения».

У меня есть образец этого кода на JSBIN:

http://jsbin.com/ucesi4/4

Кто-нибудь знает, что происходит с IE?

ОБНОВИТЬ:

Наткнулся на эту ссылку через гугл. Обратите внимание на последний комментарий:

http://msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx

Тем не менее, вы действительно должны поместить все правила стиля в HEAD для строгого соблюдения XHTML. Это также может быть немного сложно, потому что вы не можете использовать innerHTML для непосредственного внедрения в элемент HEAD или STYLE. (Оба эти тега ТОЛЬКО ДЛЯ ЧТЕНИЯ.)

Ээп! Истинный? FireFox просто слишком снисходителен? Или это просто очень странная причуда IE?

ОБНОВЛЕНИЕ 2:

Еще немного о том, что мы пытаемся здесь решить. Мы имеем дело с мобильными устройствами, и некоторые из устаревших устройств а) не поддерживают NOSCRIPT и б) имеют медленные движки JS.

Поскольку они не поддерживают NOSCRIPT, мы по умолчанию показываем ошибку, затем скрываем ее через JS, если она есть, и представляем им надлежащий контент. Из-за медленных движков JS на них люди видят «мерцание» отображения / скрытия DIV. Это было предложенное решение, чтобы справиться с этим, поскольку оно загружало бы CSS до того, как DIV даже были отрисованы.

Поскольку он кажется недействительным, решение будет заключаться в том, что на этих старых устройствах мы будем использовать этот метод (поскольку он, кажется, работает, даже если не в IE), а затем все остальные подходящие браузеры будут работать так, как было предложено ... мы Просто обновлю свойство DISPLAY CSS через встроенный JS после загрузки каждого DIV в DOM.

С учетом всего сказанного мне все еще любопытно, является ли эта проблема ошибкой IE, или IE действительно придерживается надлежащих стандартов, делая STYLE элементом только для чтения.


  • Зачем нужно добавлять <style> блок? Разве что-то вроде document.getElementById('div2').setAttribute('style', 'display:none') не будет эквивалентным? 08.03.2011
  • нам нужно выполнить это до полной загрузки DOM. Таким образом, указанные DIV недоступны для сценария во время выполнения. 08.03.2011
  • ... да, live было бы намного проще, если бы мы использовали jQuery и могли бы просто делать все это тогда ... :) 08.03.2011
  • Добро пожаловать в чудесный мир IE. 04.09.2011

Ответы:


1

В IE вы можете использовать style.styleSheet.cssText:

var style = document.createElement('style');
style.type = 'text/css';

if (style.styleSheet) { // IE
    style.styleSheet.cssText = css;
} else {
    style.appendChild(document.createTextNode(css));
}

document.getElementsByTagName('head')[0].appendChild(style);

Попробуйте это здесь: http://jsfiddle.net/QqF77/

См. Ответ на этот вопрос: Как создать тег ‹style› с Javascript

04.09.2011

2

Не используйте innerHTML, используйте document.createTextNode() и ваша жизнь станет бесконечно лучше;)

var styleNode = document.createElement('style');
styleNode.setAttribute("type", "text/css");
var textNode = document.createTextNode("#div1 {display: block;} #div2 {display: none;}");
styleNode.appendChild(textNode);
headTag.appendChild(styleNode);

РЕДАКТИРОВАТЬ:

Поскольку это решение, похоже, вам не подходит, я бы отказался от него. Вместо этого выберите решение, в котором стили уже определены и где вы просто отключили / включили стили через javascript, если они доступны.

Сделать это можно так:

<head>
<style>
.jsenabled #div2, #div1 { display: none;}
.jsenabled #div1, #div2 { display: block;}
</style>
<script>
//i know you don't use jQuery, but the solution should still be valid as a concept
//bind to DOM-ready, then set the class jsenabled on the body tag
$(function() {
 $(document.body).addClass('jsenabled');
});
</script>
</head>
<body>
<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
</body>

РЕДАКТИРОВАТЬ 2:

Если это нужно сделать до того, как DOM будет готов, вы можете сделать что-нибудь уродливое вроде этого:

<head>
<style>
#div2, .show { display: block;}
#div1, .hide { display: none;}
</style>
</head>
<body>
<div class="hide">
<script>document.write('</div><div id="div1">');</script>
   div 1 (should be shown if JS enabled)
</div>
<script>document.write('<div class="hide">');</script>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
<script>document.write('</div>');</script>
</body>

Или, чтобы упростить, вы могли бы просто сделать

<head>
<script>document.write('<style>#div1 {display: block;} #div2 {display: none;}</style>');
</head>
<body>
<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
</body>
08.03.2011
  • Я наткнулся на это и действительно возлагал большие надежды! Увы, это не решило проблему в IE. Хотя я согласен, что это более логичное решение. 08.03.2011
  • Мартин ... Я ценю усилия! Спасибо. Это сработает, но мы делаем это до полной загрузки DOM. У меня нет доступа к этим объектам DOM во время выполнения скрипта. Определенно есть гораздо более элегантное решение, и я работаю с командой, чтобы переосмыслить их настоящую проблему. На данный момент, однако, мне любопытно, является ли IE, рассматривающий STYLE как доступный только для чтения, правильным поведением или нет. 08.03.2011
  • @DA: Нет проблем. Я не знаю ответа на этот вопрос, но я обновил свой ответ другим решением, которое вы можете попробовать;) 09.03.2011

  • 3

    Лучше всего поместить всю ссылку ‹для CSS в заголовок‹, а большую часть скрипта ‹в самом конце тела‹ (за исключением коротких фрагментов ‹скрипта, которые должны выполняться как можно скорее, должны быть помещены в заголовок‹). И стандарт, и браузеры позволяют делать глупые вещи, но то, что это приемлемо, не означает, что это хорошая идея.

    Вместо того, чтобы проделывать всю эту ритуал создания узла стиля и манипулирования DOM (которые просто предоставляют больше возможностей для того, чтобы что-то пошло не так), я рекомендую гораздо более простое решение. Жестко кодируйте как можно больше в свой документ (и таблицу стилей?) Заранее один раз (вместо того, чтобы каждый раз создавать что-то на лету). Вот примерный пример:

    <body ...
    ...
    <div id="warning" style="display: block;">
    JS is required for this site, but isn't available ...
    </div>
    <div id="message" style="display: none;">
    JS is available
    </div>
    ...
    
    <script ...
    var warningEl = document.getElementById('warning');
    warningEl.style.display = 'none';
    var messageEl = document.getElementById('message');
    messageEl.style.display = 'block';
    </script ...
    

    В большинстве случаев это должно работать достаточно хорошо (определенно будет работать в большем количестве браузеров, чем то, что вы пытались сделать изначально). Однако любая попытка изменить DOM любым способом до того, как страница будет первоначально отображена для пользователя, не гарантированно работает (другими словами, ни ваш способ , ни приведенный выше пример не будет всегда работать во всех случаях). Чем раньше вы запустите свой Javascript, тем больше вероятность того, что операции DOM (включая getElementById) могут завершиться ошибкой. И чем позже вы запустите свой Javascript, тем больше вероятность, что пользователь заметит заметное «мерцание» своего дисплея. Таким образом, перед вами стоит выбор между широкой совместимостью и заметным мерцанием.

    Вы можете подождать, пока DOM будет полностью готов, прежде чем запускать Javascript («готово» в jQuery, или addEventListener «domcontextloaded», или даже addEventListener «load»). Это гарантированно работает правильно во всех случаях. Но во многих случаях он будет мерцать (возможно, довольно сильно).

    Единственный способ, который я знаю (но я надеюсь, что кто-то еще знает больше :-), чтобы полностью избежать возможности мерцания, - это поместить в ‹head фрагмент Javascript, который изменяет window.location, но ничего больше не делает . (Никаких ссылок на DOM, которые обычно становятся очевидными благодаря слову «документ» где-то в коде.) Конечным результатом, если JS доступен, будет немедленная перезагрузка другой страницы, прежде чем что-либо будет показано пользователю. Начальная страница может содержать ваше предупреждение, а новая страница - настоящий материал без предупреждений.

    Однако даже у этого метода есть некоторые недостатки: во-первых, двойная загрузка требует дополнительной полосы пропускания и немного задерживает видимость для пользователя. На обычных настольных компьютерах это не имеет значения. Но для КПК это может быть неприемлемо. Во-вторых, это вызовет хаос в SEO. Вторая страница, которая должна быть невидимой и доступна только с первой страницы, может отображаться независимо в веб-индексах, чтобы пользователи могли легко получить к ней прямой доступ (вы можете «исправить» это с помощью умного использования слов «канонический» и / или «мета ... роботы»). И поисковая выдача начальной страницы может резко упасть, когда ее единственным содержанием является предупреждающее сообщение.

    12.06.2013
    Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..