Показать сообщение отдельно
  #33 (permalink)  
Старый 08.07.2017, 23:55
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

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

Давайте попробуем создать текстовое поле, которое занимало бы ширину равную ширине введённого текста...

<input value="London" placeholder="City">
<style>
	
	input {
		/* Для начала сбросим все стили */
		all: unset;
		/* Затем применим стили, чтобы увидеть какого размера теперь поле */
		border-bottom: 0.125em dashed deeppink;
		background-color: lightpink;
		padding: 0.5em;
		font: 2em Georgia, serif;
		display: inline-block;
		min-width: 1em; /* Обратите внимание, как применилось */
		max-width: 10em;
	}
	
</style>


Каким бы не был текст, ширина упорно не меняется, по причине указанной выше. Из-за этого min-width и max-width остаются по сути не применёнными. Но давайте попробуем эмулировать текстовое поле, используя атрибут contenteditable.

<span contenteditable placeholder="City">London</span>
<style>
	
	span[contenteditable] {
		/* Затем применим стили, чтобы увидеть какого размера теперь поле */
		border-bottom: 0.125em dashed deeppink;
		background-color: lightpink;
		padding: 0.5em;
		font: 2em Georgia, serif;
		display: inline-block;
		min-width: 1em; /* Обратите внимание, как применилось */
		max-width: 10em;
		/* стили специально для contenteditable */
		white-space: nowrap;
		overflow: hidden;
		cursor: text;
	}
	
	/* Браузеры для имитаций перевода строки используют div, p или br */
	/* Также могут применять инлайн-стили, для вставленных элементов */
	span[contenteditable] * {
		white-space: nowrap !important;
		font: unset !important;
		display: inline;
	}
	
	span[contenteditable] br {
		display: none;
	}
	
	/* Имитируем placeholder */
	span[contenteditable]:empty::before {
		content: attr(placeholder);
		opacity: 0.5;
	}
	
</style>


Теперь наше текстовое поле меняет размеры, как оно должно быть на самом деле
Правда здесь проблемы:
- Браузер вставляет элементы
- В поле можно перетащить изображения и другие элементы
- Это не элемент формы (нужно будет получить значение через textContent)

Плюсы:
+ Если предположить, что пользователь будет вводить или вставлять только текст, то ОК
+ Применяются min-width и max-width, т. е. ведёт себя не странно
+ textContent возвращает только текст
+ работает placeholder, доступен через клавиатуру

В черновике спецификаций работа над которой была прекращена, определялось свойство user-modify, которое позволяет редактировать содержимое элемента, если указано значение read-write. (https://www.w3.org/TR/2000/WD-css3-u...16#user-modify)

В принципе редактирование элемента может быть задано через стили. По сути...
Код:
[contenteditable] {
	user-modify: read-write;
}
Именно так оно и работает в некоторых браузерах, например в webkit используется свойство -webkit-user-modify. В webkit используется дополнительное значение read-write-plaintext-only, которое позволяет вставлять текст без оформления.

<span contenteditable placeholder="City">London</span>
<style>
	
	span[contenteditable] {
		/* Затем применим стили, чтобы увидеть какого размера теперь поле */
		border-bottom: 0.125em dashed deeppink;
		background-color: lightpink;
		padding: 0.5em;
		font: 2em Georgia, serif;
		display: inline-grid;
		display: inline-flex;
		min-width: 1em; /* Обратите внимание, как применилось */
		max-width: 10em;
		/* стили специально для contenteditable */
		-webkit-user-modify: read-write-plaintext-only;
		cursor: text;
	}
	
	/* Имитируем placeholder */
	span[contenteditable]:empty::before {
		content: attr(placeholder);
		opacity: 0.5;
	}
	
</style>


Это всё не работает как элемент формы, но можно взять элемент формы и сделать программно вокруг него обёртку, которая позволяла бы отслеживать его размеры (при помощи стилей, обёртка скриптом).

<input value="London" placeholder="City">
<style>
	
	input, x-input {
		/* Затем применим стили, чтобы увидеть какого размера теперь поле */
		border-bottom: 0.125em dashed deeppink;
		background-color: lightpink;
		padding: 0.5em;
		font: 2em Georgia, serif;
		display: inline-block;
		min-width: 1em; /* Обратите внимание, как применилось */
		max-width: 10em;
	}
	
</style>
<script>
for(let element of document.querySelectorAll("input")) {
	let host = document.createElement("x-input")
	let parent = host.attachShadow({ mode: "open" });
	let style = document.createElement("style");
	let input = element.cloneNode(true);
	input.placeholder = element.getAttribute("placeholder");
	let field = document.createElement("span");
	
	style.textContent = `
		:host {
			position: relative;
			overflow: hidden;
		}

		input {
			all: inherit;
			margin: 0;
			border: 0;
			top: 0;
			left: 0;
			right: 0;
			bottom: 0;
			width: 100%;
			height: 100%;
			opacity: 1;
			position: absolute;
			box-sizing: border-box;
			max-width: initial;
			max-height: initial;
			min-width: initial;
			min-height: initial;
		}

		span {
			visibility: hidden;
		}
	`;
	
	element.parentNode.insertBefore(host, element.nextSibling);
	
	parent.appendChild(style);
	parent.appendChild(input);
	parent.appendChild(field);
	
	let inputHandler = () => {
		field.textContent = (input.value || input.placeholder).replace(/\s/g, "\xa0");
		element.value = input.value;
	};
	
	input.addEventListener("input", inputHandler);
	inputHandler();
	
	element.type = "hidden";
}
</script>


И ещё один способ, от Ли Веру. Тоже отслеживает размеры (при помощи скрипта).

<input value="London" placeholder="City">
<style>
	
	input {
		all: unset;
		/* Затем применим стили, чтобы увидеть какого размера теперь поле */
		border-bottom: 0.125em dashed deeppink;
		background-color: lightpink;
		padding: 0.5em;
		display: inline-block;
		font: 2em Georgia, serif;
		min-width: 1em; /* Обратите внимание, как применилось */
		max-width: 10em;
	}
	
</style>
<script src="https://leaverou.github.io/stretchy/stretchy.js"></script>


Кто знает, как ещё проще?

Определение замещаемого элемента(https://www.w3.org/TR/CSS21/conform.html, https://www.w3.org/TR/2014/REC-html5...laced-elements)
Ответить с цитированием