Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 23.09.2013, 22:33
_0_ _0_ вне форума
Аспирант
Отправить личное сообщение для _0_ Посмотреть профиль Найти все сообщения от _0_
 
Регистрация: 10.05.2013
Сообщений: 56

CSS генератор на JavaScript
Предлагаю обсудить скрипт, генерирующий CSS "на лету".

Всем понятна ущербность CSS в смысле отсутствия классов, констант и переменных (при полной поддержки во всех браузерах), особенно это ощутимо при создании какой-либо иерархии DOM-элементов (например, виджетов).

Сначала хотел сделать парсер, но в javascript проблемно подать многострочный текст при загрузке страницы, да и не силен я в парсерах, поэтому пошел объектным путем. Исходник стилевого листа - это javascript объект, с полями, заполненными по определенным правилам:
cначала идет блок констант, константы имеют флаг - первый символ "$";
потом идут классы, они начинаются с префикса "class_";
далее идут селекторы, их имена приходится заключать в кавычки.

Объект подается в генератор через конструктор function css_object( obj ), конструктор возвращает объект, который имеет метод .write(), записывающий элемент STYLE в поток документа.

Классы и селекторы - это почти то же самое, они наследуются расширяются одинаковым способом, также они имеют очень важную "вкусность" - вложенные селекторы, которые также могут расширяться классами. К дочерним селекторам прибавляется имя старшего селектора.

Константы довольно примитивны, одно правило может иметь только строку или одну константу.

Крайне чувствительный к лишним пробелам, неизбежно возникнет ошибка.

<!DOCTYPE html>
<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<title>CSS JavaScript Object</title>
	
	<script language='javascript'>
			
		function css_object( obj ){
			this.css_data=obj;
			this.trace();
		}

		css_object.prototype={
			
			// определение полей в прототипе не имеет значения, для наглядности
			css_data:null,
			// хранилища
			vars:null,
			classes:null,
			selectors:null,
			css_text:null,
			// приемник строк
			_css:null,
				
			trace:function(){
				
				var vars = this.vars={};
				var classes = this.classes = {};
				var selectors = this.selectors = {};
				var _css = this._css = [];
				var obj=this.css_data;
				
				// разбор полей на типы
				for( var p in obj ){
					if( obj.hasOwnProperty(p) ){
						var n;
						if( p.indexOf('$')==0 ){
							n=p.slice(1);
							vars[p]=obj[p];
						}else if( p.indexOf('class_')==0 ){
							n=p.slice(6)
							classes[n]=obj[p];
						}else{
							selectors[p]=obj[p]
						}
					}
				}
				
				// расширение классов
				for( var class_name in classes ){
					classes[ class_name ]=this._extend_obj( classes[ class_name ] );
				}
						
				// развертывание селекторов
				var selectors2={}; // временное хранилище развернутых селекторов
				var _this=this; // функция требует this
				function deploy_selectors( srs ){

					for( var name in srs ){
						if( srs.hasOwnProperty(name) ){
							
							var selector=srs[name];
							var deferred={}; // хранилище отложенных селекторов
							var d=false; // флаг наличия отложенных селекторов
							
							// заодно расширение селектора
							if( selector.extend ) {
								selector = _this._extend_obj( selector )
								delete selector['extend'];
							}
							
							for( var rule in selector ){
								if( selector.hasOwnProperty(rule) ){
									if( typeof selector[rule] == 'object' ){
										deferred[name+rule]=selector[rule];
										delete selector[rule];
										d=true;
									}
								}
							}
							selectors2[name]=selector;
							if( d ) deploy_selectors( deferred );
						}
					}
				}
				
				deploy_selectors( selectors );
				
				selectors = this.selectors = selectors2;
						
				// запись селекторов в массив строк
				for( var name in selectors ){
					if( selectors.hasOwnProperty(name) ){
						
						var selector=selectors[name];
						if( selector.comment ) {
							_css.push('/* '+selector.comment+' */');
							delete selector['comment'];
						}
						_css.push( name +' {' );
						for( var rule in selector ){
							if( selector.hasOwnProperty(rule) ){
								var value=selector[rule];
								if( typeof value !='object' ){
									_css.push('\t'+rule+' : '+this._parse_value( value )+';');
								}
							}
						}
						_css.push('}\n');
					}
				}

				this.css_text=_css.join('\n');
			},
			
			
			write:function(){
				document.write('<style>\n');
				document.write('/* Cгенерировано javascript */\n');
				document.write( this.css_text );
				document.write('</style>');
			},
			
			// расширение объектов стандартным способом
			_extend_obj:function( obj ){
				
				if( !obj.extend ) return obj;
				
				var top_obj=obj;
				var classes_str=obj.extend;
				obj={};
						
				var classes=this.classes;
				
				var chain=[];
				
				var parents=classes_str.split(',');
				for( var i=0, l=parents.length; i<l; i++ ){
					var class_name=parents[i];
					if( classes[class_name]  && typeof classes[class_name]=='object' ){
						chain.push( classes[class_name] )
					}else{
						var s='Не найден класс '+class_name;
						alert(e);
						throw e;
					}
				}
				chain.push( top_obj );
				
				for( var i=0, l=chain.length; i<l ;i++ ){
					this._extend( obj , chain[i] );
				}
				return obj;
			},
			
			_extend:function( obj, ext ){
				for( var n in ext ){
					if( ext.hasOwnProperty(n) ){
						if( n!='extend' ){
							var v=ext[n]
							if( v === null ){
								delete obj[n];
							}else{
								obj[n]=v;				
							}
						}
					}
				}
				return obj;
			},
			
			_parse_value:function( value ){
				
				if( value.indexOf('$')==0 ){
					
					var vars=this.vars;
					if( vars[value] ){
						return vars[value];
					}else{
						var e='Не найдена переменная '+value;
						alert(e);
						throw e;
					}
				}else{
					return value;
				}
			},
		}


	</script>
	<script language='javascript'>
	
		css=new css_object({
			
			// константы
			
			$widget_border:"1px solid #888",
			$widget_color:"green /* $widget_color */",
			$widget_background_color:"yellow",
			
			$widget_margin:"3px /* $widget_margin */",
			$widget_padding:"1px /* $widget_padding */",
			
			$widget_hover_color:"red /* $widget_hover_color */",
			
			// классы
			
			class_widget:{
				
				"border":"$widget_border",
				"color":"$widget_color",
				"background-color":"$widget_background_color",
				
				"font-family":"Arial",
			},
			
			class_widget_block: { extend:"widget",
			
				"display":"block",
				
				"width":"200px",
				"height":"50px",
				
				"margin":"3px",
				"padding":"3px",
				
				":hover":{
					comment:"Обработка наведения курсора на стандартный блок widget",
					"border-color":"$widget_hover_color"
				}
			},
				
			class_widget_hover:{
				"text-decoration":"none",
				"color":"blue",		
				':hover':{
					comment:"Обработка наведения курсора",
					"text-decoration":"underline",
					"color":"$widget_hover_color",
				},
			},
			
			class_clear_list:{
				comment:"Чистка стиля списков",
				"display":"block",
				"padding":"0px",
				"margin":"0px",
				"list-style-type":"none",
			},
			
			// селекторы
			
			".widget":{ extend:"widget_block",
				
				" .label":{
					"font-size":"12px"
				},
				" a":{ extend:"widget_hover",
					"font-size":"12px",
					"color":"blue"
				},
				" button":{ extend:"widget_hover",
					"font-weight":"bold"
				},
				" div.menu":{
					comment:"Менюшка на css",
					
					"position":"relative",
					"display":"inline-block",
					
					" ul,li":{ extend : "clear_list" },			
								
					" ul":{
						comment:"Все выпадающие блоки",
						"position":"absolute",
						"width":"150px",
						"background-color":"$widget_background_color",
						"display":"none",
					},			
								
					" .label":{ extend : "widget_hover",
						
						"position":"relative",
						
						"line-height":"20px",
						"border":"$widget_border",
						"padding":"0px 5px 0px 5px",
						"cursor":"pointer",
						
						":hover > ul":{
							comment:"Выпадание вложенного блока",
							"display":"block",
							"top":"-1px",
							"left":"100%"
						}
						
					},

					" > ul":{
						comment:"Первый элемент меню",
						"top":"20px",
						"left":"0px",
					},
										
					":hover > ul":{
						comment:"Выпадание первого элемента",
						"display":"block",
					},

				}
			},
			
			
			"textarea#t":{
				comment:"Элемент вывода",
				"width":"600px",
				"height":"300px"
			
			},
			
			
		})

		css.write()

	
	</script>
	
	</head>
	
<body>
<div class='widget'>
	<span class='label'>просто виджет '.widget'</span><br />
	<a href='#'>Ссылка</a>
</div>
<div class='widget'>
	<span class='label'>виджет с кнопкой</span><br />
	<button>Button</button>
</div>
<div class='widget'>
	<span class='label'>виджет с css меню</span><br />
	<div class='menu'>
		<span class='label'>Меню</span>
		<ul>
			<li class='label'>Пункт 1</li>
			<li class='label'>Пункт 2 ...
				<ul>
					<li class='label'>Пункт 2-1</li>
					<li class='label'>Пункт 2-2</li>
					<li class='label'>Пункт 2-3 ...
						<ul style='width:100px'>
							<li class='label'>Пункт 2-3-1</li>
							<li class='label'>Пункт 2-3-1</li>
						</ul>
					</li>
					<li class='label'>Пункт 2-4</li>
				</ul>
			</li>
			<li class='label'>Пункт 3</li>
			<li class='label'>Пункт 4</li>
			
		</ul>
	</div>
</div>
<hr />
<textarea id='t' -style='width:500px;height:300px'></textarea>
<script language='javascript'>
	document.getElementById('t').value=css.css_text;
</script>
</body>
</html>
Ответить с цитированием
  #2 (permalink)  
Старый 23.09.2013, 22:34
_0_ _0_ вне форума
Аспирант
Отправить личное сообщение для _0_ Посмотреть профиль Найти все сообщения от _0_
 
Регистрация: 10.05.2013
Сообщений: 56

Работает без регулярок, вроде быстро, возможно подойдет не только в период разработки, но и для клиента. А что? При определенных навыках, можно изменять объект, в зависимости от "внешних" условий, например версии браузера или размера монитора..
Ответить с цитированием
  #3 (permalink)  
Старый 23.09.2013, 23:00
Аватар для kobezzza
Быдлокодер;)
Отправить личное сообщение для kobezzza Посмотреть профиль Найти все сообщения от kobezzza
 
Регистрация: 19.11.2010
Сообщений: 4,338

http://learnboost.github.io/stylus/
__________________
kobezzza
code monkey
Ответить с цитированием
  #4 (permalink)  
Старый 23.09.2013, 23:13
_0_ _0_ вне форума
Аспирант
Отправить личное сообщение для _0_ Посмотреть профиль Найти все сообщения от _0_
 
Регистрация: 10.05.2013
Сообщений: 56

Это не тоже самое. Я изначально хотел генерировать css в браузере, не на сервере, да и на большой скорости.
Ответить с цитированием
  #5 (permalink)  
Старый 24.09.2013, 05:44
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

а зачем именно на клиенте?
Ответить с цитированием
  #6 (permalink)  
Старый 24.09.2013, 06:36
_0_ _0_ вне форума
Аспирант
Отправить личное сообщение для _0_ Посмотреть профиль Найти все сообщения от _0_
 
Регистрация: 10.05.2013
Сообщений: 56

А почему бы и нет, все под рукой.
Ответить с цитированием
  #7 (permalink)  
Старый 24.09.2013, 10:54
Аватар для kobezzza
Быдлокодер;)
Отправить личное сообщение для kobezzza Посмотреть профиль Найти все сообщения от kobezzza
 
Регистрация: 19.11.2010
Сообщений: 4,338

Сообщение от _0_ Посмотреть сообщение
Это не тоже самое. Я изначально хотел генерировать css в браузере, не на сервере, да и на большой скорости.
Stylus написан на JS, т.е. его также можно использовать на клиенте. Другое дело что такой подход используют лишь при разработке, но никак не на продакшене, т.к. это абсолютно не нужные затраты ресурсов клиента.
__________________
kobezzza
code monkey
Ответить с цитированием
  #8 (permalink)  
Старый 24.09.2013, 13:07
Аватар для Shaci
:-/
Отправить личное сообщение для Shaci Посмотреть профиль Найти все сообщения от Shaci
 
Регистрация: 28.09.2009
Сообщений: 1,126

Сообщение от _0_
Я изначально хотел генерировать css в браузере, не на сервере, да и на большой скорости
не стоит это делать на клиенте
Ответить с цитированием
  #9 (permalink)  
Старый 24.03.2018, 11:54
Новичок на форуме
Отправить личное сообщение для Romario5 Посмотреть профиль Найти все сообщения от Romario5
 
Регистрация: 24.03.2018
Сообщений: 1

А по моему отличная идея. Можно генерировать не все css, а только те, которые необходимы. Например пользователь перешел в профиль - сгенерировались css для профиля. Плюс ко всему можно менять темы на лету без препроцессоров.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
javascript в классе CSS Golovastik Общие вопросы Javascript 3 23.02.2011 03:00
Последние книги по JavaScript! monolithed Учебные материалы 7 26.10.2010 19:40
Выдвет ошибку JavaScript Ромио Opera, Safari и др. 4 21.10.2010 20:34
Выпадающее меню на css и javascript Jackky Общие вопросы Javascript 3 13.09.2008 18:30
javascript, css и iexplorer Блондинко Internet Explorer 4 21.02.2008 12:39