Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Динамическое изменение ширины в зависимости от содержимого (https://javascript.ru/forum/events/22703-dinamicheskoe-izmenenie-shiriny-v-zavisimosti-ot-soderzhimogo.html)

trikadin 03.11.2011 13:43

Цитата:

Сообщение от tenshi
трудно забить на тех с кем приходится работать.. особенно на начальника..

А, ну если так, то это, конечно, да... Но я говорил про людей на форуме)

boor1 07.07.2017 15:52

******* по сабжу ******

Правильное (логичное) решение - через CSS:
нужно (как-то) заставить <INPUT> вести себя как <SPAN>

Свойство display=inline у обоих.
Нужно что-то ещё.

Или это невозможно задать параметрически для <INPUT>?

Malleys 08.07.2017 23:55

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

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

<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)

Маэстро 24.07.2017 18:17

Цитата:

Сообщение от Malleys (Сообщение 457990)
Кто знает, как ещё проще?

Старая тема (6 лет назад подняли). Но если кому-то нужно поделюсь своим вариантом. Фишка в создании "теневого спана" - копии инпута. Браузер сам вычисляет ширину элемента в зависимости от названия шрифта, его размера и длины текстовой строки. Нам надо её просто взять.
Свойства стиля в <span> и <input> должны полностью совпадать (некоторые браузеры не наследуют в input font-family по inherit).
<html>
<style>
.inp {font-family:calibri,arial,sans-serif; font-size:1em; padding:.2px; background-color:#DDDDFF; border:1px solid #F00; min-width:1em}
</style>
<body>
<script>
function rsz()
{
setTimeout(function(){
 var inp = document.getElementById('inp1');
 var spa = document.getElementById('inp1copy');
 ///spa.innerText = inp.value; // так не учитывает пробелы
 spa.innerText = inp.value.replace(/\s/ig, String.fromCharCode(160));
 inp.style.width=spa.offsetWidth + 20; // 20px чтобы инпут не дергался при вводе нового символа
                     },0);
};

</script>

<input id="inp1" class="inp" style="width:10px" type="input" value="" onkeydown="rsz()" onpaste="rsz()" oncut="rsz()">
<span id="inp1copy" class="inp" style="position:absolute; left:-30000px; top:0px"> </span>
</body>
</html>

laimas 24.07.2017 19:01

Цитата:

Сообщение от Маэстро
/\s/i

А пробел может иметь верхний и нижний регистры? :)

Маэстро 24.07.2017 19:06

Цитата:

Сообщение от laimas (Сообщение 459449)
А пробел может иметь верхний и нижний регистры? :)

ну погорячился.. писал по памяти из проекта двухлетней давности..
собственно, для понимания идеи это не важно. хотя Ваше замечание об аккуратности оформления кода учту

laimas 24.07.2017 19:23

Цитата:

Сообщение от Маэстро
замечание об аккуратности

Тут более правильно говорить о нагрузке, так не "учитывать регистр" означает дополнительное действие над символом. Или например, если говорить о более мощном REGEXP, чего нет в JS, но так, к слову, то для нахождения английских символов в юникод строке также не обязательно указывать модификатор u, так как этот набор в строке будет все равно однобайтовым.

Маэстро 24.07.2017 19:28

Цитата:

Сообщение от laimas (Сообщение 459454)
Тут более правильно говорить о нагрузке

К нагрузке я тоже всегда отношусь очень щепетильно. Но это не тот случай. После нажатия на клавишу (даже в режиме автоповтора) даже самый слабый процессор успеет сделать замену символа. Речь не идет о 100000 операциях в цикле. 100 ms между нажатиями клавиш ему хватит с головой. Это просто "опечатка", не ведущая ни к каким печальным последствиям, тем более в учебном примере.

laimas 24.07.2017 19:38

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


Часовой пояс GMT +3, время: 10:07.