Welcome to the nuBuilder Forums!

Register and log in to access exclusive forums and content available only to registered users.

Label aligning/positioning

Post your ideas & suggestions for new nuBuilder features or other improvements
Uzlander
Posts: 52
Joined: Sat Jul 08, 2023 10:21 am
Has thanked: 7 times
Been thanked: 2 times

Label aligning/positioning

Unread post by Uzlander »

Hi there! Recently have been trying to align labels (on the form) nicely on the left side and effectively failed.
Sometimes it seems prettier to have labels all placed left on a single vertical (even though different space between corresponding input).
Last thing I've tried was (css)

Code: Select all

left: unset !important;
and the result wasn't very eye peasing either.
Now we have nice solution to place labels on top (very handy 'nuLabelOnTop()' method) whereas by default right alignment's used, but no built in solution for left alignment.
So if someone can come up with this tricky thing please share.
Generally I think it would be nice to have some built method/solution for this as well
kev1n
nuBuilder Team
Posts: 4428
Joined: Sun Oct 14, 2018 6:43 pm
Has thanked: 74 times
Been thanked: 475 times
Contact:

Re: Label aligning/positioning

Unread post by kev1n »

I think that's a good idea.
We just need to consider how to control the label's width — especially what should happen if the text gets too long:
What’s a reasonable maximum width? Should it wrap to a new line, and if so, at what point?
If anyone has ideas or would like to contribute a pull request, it would be very welcome.
uzlander_ya
Posts: 6
Joined: Thu Apr 24, 2025 8:00 pm
Has thanked: 2 times

Re: Label aligning/positioning

Unread post by uzlander_ya »

I see, well my immediate answer/take on this is - width of the widest label on the vertical or even dummier - autofill the shorter labels with empty-space symbols to equal the longest among them, so we get the desired look on the form.
I don't think of wrapping cos to me its gonna look less elegant then and probably its little problematic to align two or more line label perfectly against its very input field. Better go for 'labelsOnTop' instead, here (long living in an era of small screens) we need to consider mobile cases right from the gate.
Currently with mostly PC/Laptop size screens being on my mind at this point its ok for me to have one-line labels all neatly aligned left and make it resemble old ms access tutorials :))
kev1n
nuBuilder Team
Posts: 4428
Joined: Sun Oct 14, 2018 6:43 pm
Has thanked: 74 times
Been thanked: 475 times
Contact:

Re: Label aligning/positioning

Unread post by kev1n »

Give this a go:

Add this function under Setup → Header, and then log in again into nuBuilder.

Code: Select all

function nuLabelLeftAligned(include, exclude, offsetTop = 0, offsetLeft = -100) {
	include = include || nuSERVERRESPONSE.objects.map(obj => obj.id);
	exclude = exclude || [];
	for (let i = 0; i < include.length; i++) {
		if (jQuery.inArray(include[i], exclude) == -1) {
			$element = $('#' + include[i]);
			$('#' + 'label_' + include[i]).css({
				'top': $element.nuCSSNumber('top') + offsetTop,
				'left': $element.nuCSSNumber('left') + offsetLeft,
				'text-align': 'left'
			});
			$element.attr('data-nu-label-position', 'left');
		}
	}
}

Next, in the form where you want the labels to be left-aligned, add the following line under Custom Code:

nuLabelLeftAligned();


Or if you would like custom horizontal spacing (150px to the left)

nuLabelLeftAligned(null, null, 0, -150);


And to include specific elements with custom positioning, use the function like this

nuLabelLeftAligned(['field1', 'field2'], [], 5, -120);


Some more usage examples:

Set max width to 80px with text wrapping

nuLabelLeftAligned(null, null, 0, -100, 80);

Custom positioning with 120px max width

nuLabelLeftAligned(['field1', 'field2'], [], 5, -150, 120);

Specific elements with tight label width

nuLabelLeftAligned(['username', 'password'], [], 0, -90, 60);

The text will now wrap to multiple lines if it exceeds the specified maxWidth, keeping your labels neat and preventing them from overlapping with other elements.


What it looks like:

left-aligned labels.png
You do not have the required permissions to view the files attached to this post.
kev1n
nuBuilder Team
Posts: 4428
Joined: Sun Oct 14, 2018 6:43 pm
Has thanked: 74 times
Been thanked: 475 times
Contact:

Re: Label aligning/positioning

Unread post by kev1n »

Then you can also test these functions in a similar way:

Code: Select all

function nuLabelLeftAligned(include, exclude, offsetTop = 0, offsetLeft = 'auto', maxWidth = null, gap = 10) {
	include = include || nuSERVERRESPONSE.objects.map(obj => obj.id);
	exclude = exclude || [];
	
	for (let i = 0; i < include.length; i++) {
		if (jQuery.inArray(include[i], exclude) == -1) {
			$element = $('#' + include[i]);
			$label = $('#' + 'label_' + include[i]);
			
			let leftPosition;
			
			// Determine optimal left position
			if (offsetLeft === 'auto') {
				// Method 1: Calculate based on actual label width
				let labelWidth = $label.outerWidth();
				if (labelWidth === 0) {
					// If label not rendered yet, use text length estimation
					let labelText = $label.text();
					let estimatedWidth = labelText.length * 8; // rough estimation: 8px per character
					labelWidth = Math.min(estimatedWidth, maxWidth || 200);
				}
				leftPosition = $element.nuCSSNumber('left') - labelWidth - gap;
			} else if (typeof offsetLeft === 'string' && offsetLeft.includes('%')) {
				// Method 2: Percentage-based positioning relative to element width
				let percentage = parseFloat(offsetLeft.replace('%', '')) / 100;
				let elementWidth = $element.outerWidth();
				leftPosition = $element.nuCSSNumber('left') - (elementWidth * percentage);
			} else {
				// Method 3: Fixed offset (original behavior)
				leftPosition = $element.nuCSSNumber('left') + offsetLeft;
			}
			
			let cssProperties = {
				'top': $element.nuCSSNumber('top') + offsetTop,
				'left': leftPosition,
				'text-align': 'left'
			};
			
			// Add max-width and text wrapping if specified
			if (maxWidth !== null && maxWidth > 0) {
				cssProperties['max-width'] = maxWidth + 'px';
				cssProperties['white-space'] = 'normal';
				cssProperties['word-wrap'] = 'break-word';
				cssProperties['overflow-wrap'] = 'break-word';
			}
			
			$label.css(cssProperties);
			$element.attr('data-nu-label-position', 'left');
		}
	}
}


Alternative approach: Calculate optimal positioning for all labels collectively

Code: Select all

function nuLabelLeftAlignedOptimal(include, exclude, offsetTop = 0, maxWidth = null, gap = 10) {
	include = include || nuSERVERRESPONSE.objects.map(obj => obj.id);
	exclude = exclude || [];
	
	// First pass: collect all label widths
	let labelWidths = [];
	let maxLabelWidth = 0;
	
	for (let i = 0; i < include.length; i++) {
		if (jQuery.inArray(include[i], exclude) == -1) {
			$label = $('#' + 'label_' + include[i]);
			let labelWidth = $label.outerWidth();
			
			if (labelWidth === 0) {
				// Estimate width if not rendered
				let labelText = $label.text();
				labelWidth = Math.min(labelText.length * 8, maxWidth || 200);
			}
			
			labelWidths.push(labelWidth);
			maxLabelWidth = Math.max(maxLabelWidth, labelWidth);
		}
	}
	
	// Second pass: position all labels using the maximum width found
	let labelIndex = 0;
	for (let i = 0; i < include.length; i++) {
		if (jQuery.inArray(include[i], exclude) == -1) {
			$element = $('#' + include[i]);
			$label = $('#' + 'label_' + include[i]);
			
			// Use consistent positioning based on widest label
			let leftPosition = $element.nuCSSNumber('left') - maxLabelWidth - gap;
			
			let cssProperties = {
				'top': $element.nuCSSNumber('top') + offsetTop,
				'left': leftPosition,
				'text-align': 'left',
				'width': maxLabelWidth + 'px' // Ensure consistent width
			};
			
			// Add max-width and text wrapping if specified
			if (maxWidth !== null && maxWidth > 0) {
				cssProperties['max-width'] = Math.min(maxLabelWidth, maxWidth) + 'px';
				cssProperties['white-space'] = 'normal';
				cssProperties['word-wrap'] = 'break-word';
				cssProperties['overflow-wrap'] = 'break-word';
			}
			
			$label.css(cssProperties);
			$element.attr('data-nu-label-position', 'left');
			labelIndex++;
		}
	}
}


nuLabelLeftAligned (Dynamic positioning)
Best practices implemented:
  • Auto-calculation: Set offsetLeft = 'auto' to automatically calculate position based on actual label width
  • Percentage-based: Use offsetLeft = '50%' for positioning relative to element width
  • Gap parameter: gap parameter for consistent spacing between label and element
  • Fallback estimation: If label isn't rendered yet, estimates width based on text length
nuLabelLeftAlignedOptimal (Consistent alignment)
Best for forms with multiple fields:
  • Two-pass approach: First calculates all label widths, then positions them consistently
  • Uniform alignment: All labels align to the same left position (based on widest label)
  • Consistent width: All labels get the same width for perfect alignment
Usage Examples:

Auto-calculate optimal distance
nuLabelLeftAligned(null, null, 0, 'auto', 120, 5);

Percentage-based positioning
nuLabelLeftAligned(null, null, 0, '75%', 100);

Consistent alignment for all labels (best for forms)
nuLabelLeftAlignedOptimal(null, null, 0, 150, 8);


Best Practices Summary:
  • Use 'auto' for individual optimal positioning
  • Use percentage for responsive layouts
  • Use nuLabelLeftAlignedOptimal for forms where you want perfect label alignment
  • Set appropriate gap values (5–15px typically works well)
  • Consider maxWidth to prevent labels from being too wide on different screen sizes
The optimal approach depends on your specific use case — individual dynamic positioning vs. consistent form alignment.
Uzlander
Posts: 52
Joined: Sat Jul 08, 2023 10:21 am
Has thanked: 7 times
Been thanked: 2 times

Re: Label aligning/positioning

Unread post by Uzlander »

Waw, such a great work, great many thanks! I believe this will be included in the docs. The method/function i was particularly looking for is nuLabelLeftAlignedOptimal() - works pretty good for me.
Generally i love this forum, the place were i always (so far) get questions answered even if i fail to formulate some properly :).
My gratitude
kev1n
nuBuilder Team
Posts: 4428
Joined: Sun Oct 14, 2018 6:43 pm
Has thanked: 74 times
Been thanked: 475 times
Contact:

Re: Label aligning/positioning

Unread post by kev1n »

Thanks for testing! Please let me know if you have any further feedback or suggestions for improvement. Once the proof testing is complete, we’ll look into how to incorporate it into nuBuilder.
Uzlander
Posts: 52
Joined: Sat Jul 08, 2023 10:21 am
Has thanked: 7 times
Been thanked: 2 times

Re: Label aligning/positioning

Unread post by Uzlander »

Another minor issue i faced, dragging those fields (&their corresponding labes by extension) suddenly labels totally disappear from view. Although with 'nuLabelsOnTop' applied forms it bahaves ok inside as well as ut of ContentBox
kev1n
nuBuilder Team
Posts: 4428
Joined: Sun Oct 14, 2018 6:43 pm
Has thanked: 74 times
Been thanked: 475 times
Contact:

Re: Label aligning/positioning

Unread post by kev1n »

Yes, dragging objects with left-aligned labels requires separate handling to ensure their positions are preserved during the drag operation. At this stage, the primary function serves mainly as a feasibility test. The next step is to fully integrate this functionality, allowing label positions to be also configurable via an attribute. This is already partially supported — for example, the label position can currently be set to values like "top" instead of the default "right-aligned". Once fully implemented, this will ensure consistent support for both label positioning and object dragging.
Uzlander
Posts: 52
Joined: Sat Jul 08, 2023 10:21 am
Has thanked: 7 times
Been thanked: 2 times

Re: Label aligning/positioning

Unread post by Uzlander »

Here's some dummy workaround i'm going to use in some cases. Maybe someone else benefits from too :)

Code: Select all

function nuLabelLeftAlignDummy(gap = 8) {
    let maxLabelTextLength = 0;
    let labelsToProcess = []; // Для хранения ссылок на jQuery-объекты меток и их оригинального текста

    // 1. Первый проход: Найти метку с максимальным количеством символов и сохранить ссылки на метки
    $('label[id^="label_"]').each(function() {
        let $label = $(this);
        let labelText = $label.text();
        
        labelsToProcess.push({
            $element: $label,
            originalText: labelText,
            associatedElementId: $label.attr('id').replace('label_', '') // Получаем ID связанного элемента
        });

        if (labelText.length > maxLabelTextLength) {
            maxLabelTextLength = labelText.length;
        }
    });

    // 2. Второй проход: Дополнить текст меток пробелами и определить фактическую ширину
    let actualUnifiedLabelWidth = 0; 
    
    labelsToProcess.forEach(function(labelInfo) {
        let $label = labelInfo.$element;
        let currentText = labelInfo.originalText;

        if (currentText.length < maxLabelTextLength) {
            let spacesToAdd = maxLabelTextLength - currentText.length;
            let nbsp = '&nbsp;'.repeat(spacesToAdd);
            $label.html(currentText + nbsp); 
        } else {
            $label.html(currentText); 
        }
        
        // Убедимся, что пробелы корректно отображаются.
        // Это свойство должно быть установлено до измерения outerWidth для точности.
        $label.css('white-space', 'pre'); 

        // После изменения текста и установки white-space, измеряем актуальную ширину.
        // Делаем это внутри цикла, чтобы получить точную ширину после рендеринга.
        let currentLabelWidth = $label.outerWidth();
        if (currentLabelWidth > actualUnifiedLabelWidth) {
            actualUnifiedLabelWidth = currentLabelWidth;
        }
    });

    // 3. Третий проход: Позиционирование всех меток, используя найденную актуальную ширину
    labelsToProcess.forEach(function(labelInfo) {
        let $label = labelInfo.$element;
        let $associatedElement = $('#' + labelInfo.associatedElementId); // Получаем связанный элемент

        // Проверяем, существует ли связанный элемент
        if ($associatedElement.length) {
            // Предполагаем, что nuCSSNumber доступен для получения числовых значений left/top
            // Если nuCSSNumber недоступен, его можно заменить на $associatedElement.position().left
            let elementLeft = $associatedElement.nuCSSNumber ? $associatedElement.nuCSSNumber('left') : $associatedElement.position().left;
            let elementTop = $associatedElement.nuCSSNumber ? $associatedElement.nuCSSNumber('top') : $associatedElement.position().top;

            let leftPosition = elementLeft - actualUnifiedLabelWidth - gap;
            
            let cssProperties = {
                'top': elementTop, // Здесь offsetTop не используется, т.к. мы его убрали. Можно вернуть если нужно.
                'left': leftPosition,
                'text-align': 'left',
                'width': actualUnifiedLabelWidth + 'px' 
            };
            
            $label.css(cssProperties);
            $associatedElement.attr('data-nu-label-position', 'left');
        }
    });
}
Post Reply