Jump to content

Select input customization for long text options


Recommended Posts

Hi there!

I had a use case where drop-down input contained long lines of text. By default, a long line of text inside select input is getting cropped at the right.
But it was required to keep the full text visible. I wrote the script that can be used as it is to solve this problem. Just copy and paste the code below into your form data page header.

 

<script>

document.addEventListener('DataPageReady', _=>{

const mainFunction = _=> {
hideSelectContainers()
addVirtualSelectOptions()
addVirtualSelectOptionsListeners()
addVirtualSelectOptionsContainerListeners()
addStyles()
}

const hideSelectContainers = _=> {
document.querySelectorAll('.cbFormSelect').forEach(item=> {item.style = "display: none;"})
}

const virtualSelectContainerGenerator = selectElement => {
    let template = '';
    selectElement.querySelectorAll('option').forEach(option=>{template += `<div value="${option.getAttribute('value')}" class="virtualOption">${option.innerHTML}</div>`; option.classList.add("hideElement")})
    return `<div class="virtualSelectContainer"> 
               <div class="virtualSelect">
                <span class="virtualSelectText">${selectElement.value}</span>
               <div class="virtualOptionContainer hideElement">${template}</div>
               </div><span class="custom-arrow"></span></div>`;
}

const addVirtualSelectOptions = _=> {
document.querySelectorAll('[class*="cbFormBlock"]').forEach( item =>{
if(item.querySelector('select')!=null) {
item.insertAdjacentHTML('beforeend', virtualSelectContainerGenerator(item.querySelector('select')))
}
})
}

const addVirtualSelectOptionsListeners = _=> {
document.querySelectorAll('.virtualOption').forEach( option => {
option.addEventListener('click', e=> {
    let realOption = document.querySelector(`[value="${e.target.getAttribute('value')}"]`);
    realOption.parentElement.selectedIndex = realOption.index 
    e.target.parentElement.parentElement.querySelector('.virtualSelectText').innerText = e.target.innerText;
    e.target.parentElement.classList.toggle("hideElement");
  })
})
}

const addVirtualSelectOptionsContainerListeners = _=> {
 document.querySelectorAll('.virtualSelectContainer').forEach( item =>{
item.addEventListener('click', e => {
  let clickedElement = e.target;
  if (clickedElement.classList.contains('virtualSelectContainer')) {
  clickedElement.querySelector('.virtualOptionContainer').classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('virtualSelectText')) {
  clickedElement.nextElementSibling.classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('custom-arrow')) {
  clickedElement.previousElementSibling.classList.toggle('hideElement');
  }
 }, true)
})
}

const addStyles = _=> {

let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
.virtualSelectContainer {
position: relative;
max-width: 800px;
--custom-paddings: 1em 5.5em 1em 1.5em;
box-sizing: border-box;
color: white;
white-space: normal;
}
.virtualSelectContainer:hover {
cursor: pointer;
}
.virtualSelect {
box-shadow: 0 10px 20px rgba(0,0,0,1);
font-size: 1.1rem;
background: #4d5061;
border: 0;
max-width: 100%;
}
.hideElement {
display: none;
}
.virtualOption {
padding: var(--custom-paddings);
margin-top: 10px;
overflow: hidden;

}
.virtualOption:hover {
  background: #4A65FF;
  color: white;
}
.virtualSelectText {
display: block;
padding: var(--custom-paddings);
}
.custom-arrow {
  display: block;
  background-color: #3b3c47;
  height: 100%;
  width: 5rem;
  position: absolute;
  right: 0;
  top: 0;
  pointer-events: none;
}
.custom-arrow::before, .custom-arrow::after {
  z-index: 10;
  --size: .65em;
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  left: 50%;
  transform: translate(-50%, -50%);
}
.custom-arrow::before {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-bottom: var(--size) solid rgba(255,255,255,.5);
top: 40%;
}
.custom-arrow::after {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-top: var(--size) solid rgba(255,255,255,.5);
 top: 60%;
}
.cbSubmitButtonContainer .cbSubmitButton {
background-color: #4d5061;
box-shadow: 0 10px 20px rgba(0,0,0,1);
}
.cbSubmitButtonContainer .cbSubmitButton:hover {
background-color: #4A65FF;
}`;
document.querySelector('head').appendChild(style);}

mainFunction()

})
</script>


It is currenlty deployed on this test page: https://c7eku786.caspio.com/dp/7f80b000aa77201adc02489f8c6d

Hope someone will find this useful.

Link to comment
Share on other sites

Hello Volomeister

this is exactly what I am having trouble with. I need to be able to see all the posibilities that are in dropdown and are longer text  in 2 or more lines depending on how long the text in te field is.

I have tried your code, but for some reason it does not work for me.

maybe because:

1. I need to use it for cascading dropdown

2. I need to use it in Bulk edit window

3. I need to use it in a field that is in Details datapage, or tabular report datapage after I click detail of certain record.

 

any suggestion?

 

thanks for any help

Link to comment
Share on other sites

Hello Volomeister

thank you for your reply.

I can not really give you the link to the page or copy of it because it is linked to the table with real data,

anyway, i have made an example of a details datapage.

here you have 2 different links.

http://www.spravaregistratury.sk/index.php/test2/

http://www.spravaregistratury.sk/index.php/test3/

 

on these two links are 2 datapages which are exactly the same, linked to the same source. but one version has your code in header, another does not.

you will be asked for ID - please use this 845099866

after you click "Hladaj" you will get to the details datapage.

 

I can make same for bulk edit datapage. just let me know if this is ok to check.

 

thank you very much Volomeister.

 

 

Link to comment
Share on other sites

Hi @Lukas,

I was trying to open those links but they are not working now. Can you check?

Cascading drop-down is a bit more complicated to implement.  I did some changes so it works for cascading drop downs on details page. You can test it here: https://c7eku786.caspio.com/dp/7f80b0001b3b2dea591d4e7cb249

Updated script is here:

 

<script>

const changeHandler = e => {

  let selectElement = e.target;
  let selectElementValue = selectElement.value
  let virtualSelectContainer = document.querySelector(`[virtual-attribute="${selectElement.getAttribute('id')}"]`)
  if(selectElementValue=='') {
  selectElementValue+= selectElement.querySelector('option[value=""]').innerText
  }
  virtualSelectContainer.querySelector('.virtualSelectText').innerText = selectElementValue;
  virtualSelectContainer.querySelector('.virtualOptionContainer').innerHTML = virtualSelectOptionsGenerator(selectElement).template
  
}


const hideSelectContainers = _=> {
document.querySelectorAll('.cbFormSelect').forEach(input => {
input.style = "display: none;"
input.addEventListener('change', changeHandler)
})
}

const virtualSelectOptionsGenerator = selectElement => { 
    let template = '';
    selectElement.querySelectorAll('option').forEach(option=>{template += `<div value="${option.getAttribute('value')}" class="virtualOption" onclick="virtualOptionClickHandler(this)">${option.innerHTML}</div>`; option.classList.add("hideElement")})
    return {
            template,
            selectElement
    }
}


const virtualSelectContainerGenerator = optionsObj => {

  return `<div class="virtualSelectContainer" virtual-attribute="${optionsObj.selectElement.getAttribute('id')}"> 
               <div class="virtualSelect">
                <span class="virtualSelectText">${optionsObj.selectElement.value}</span>
                <div class="virtualOptionContainer hideElement">${optionsObj.template}</div>
               </div><span class="custom-arrow"></span></div>`;
}

const addVirtualSelectContainer = _=> {
document.querySelectorAll('[class*="cbFormBlock"]').forEach( item =>{
if(item.querySelector('select')!=null) {
      if(document.querySelector(`[virtual-attribute="${item.querySelector('select').getAttribute('id')}"]`) == null) {
      item.insertAdjacentHTML('beforeend', virtualSelectContainerGenerator(virtualSelectOptionsGenerator(item.querySelector('select'))))
      
      }
}
})
}

const updateVirtualSelectOptions = virtualSelectContainer => {
  
const selectElement = document.querySelector(`#${virtualSelectContainer.getAttribute('virtual-attribute')}`);
virtualSelectContainer.querySelector('.virtualOptionContainer').innerHTML = virtualSelectOptionsGenerator(selectElement).template;
virtualSelectContainer.querySelector('.virtualSelectText').innerText = selectElement.value;
addVirtualSelectOptionsContainerListeners()
}


const addVirtualSelectOptionsListeners = _=> {
  
document.querySelectorAll('.virtualOption').forEach( option => {
option.addEventListener('click', e=> {
    let realOption = document.querySelector(`[value="${e.target.getAttribute('value')}"]`);
    realOption.parentElement.selectedIndex = realOption.index 
    e.target.parentElement.parentElement.querySelector('.virtualSelectText').innerText = e.target.innerText;
    e.target.parentElement.classList.toggle("hideElement");
    realOption.dispatchEvent(new Event('change'))

  })
})
}



const virtualOptionClickHandler = function(e) {
  
    let realOption = document.querySelector(`[value="${e.getAttribute('value')}"]`);
    realOption.parentElement.selectedIndex = realOption.index 
    e.parentElement.parentElement.querySelector('.virtualSelectText').innerText = e.innerText;
    e.parentElement.classList.toggle("hideElement");
    realOption.parentElement.dispatchEvent(new Event('change'))
}

const addVirtualSelectOptionsContainerListeners = _=> {
 document.querySelectorAll('.virtualSelectContainer').forEach( item =>{
item.addEventListener('click', e => {
  let clickedElement = e.target;
  if (clickedElement.classList.contains('virtualSelectContainer')) {
  clickedElement.querySelector('.virtualOptionContainer').classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('virtualSelectText')) {
  clickedElement.nextElementSibling.classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('custom-arrow')) {
  clickedElement.previousElementSibling.classList.toggle('hideElement');
  }
 }, true)
})
}

const addStyles = _=> {

let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
.virtualSelectContainer {
position: relative;
max-width: 800px;
--custom-paddings: 1em 5.5em 1em 1.5em;
box-sizing: border-box;
color: white;
white-space: normal;
}
.virtualSelectContainer:hover {
cursor: pointer;
}
.virtualSelect {
box-shadow: 0 10px 20px rgba(0,0,0,1);
font-size: 1.1rem;
background: #4d5061;
border: 0;
max-width: 100%;
}
.hideElement {
display: none;
}
.virtualOption {
padding: var(--custom-paddings);
margin-top: 10px;
overflow: hidden;

}
.virtualOption:hover {
  background: #4A65FF;
  color: white;
}
.virtualSelectText {
display: block;
padding: var(--custom-paddings);
}
.custom-arrow {
  display: block;
  background-color: #3b3c47;
  height: 100%;
  width: 5rem;
  position: absolute;
  right: 0;
  top: 0;
  pointer-events: none;
}
.custom-arrow::before, .custom-arrow::after {
  z-index: 10;
  --size: .65em;
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  left: 50%;
  transform: translate(-50%, -50%);
}
.custom-arrow::before {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-bottom: var(--size) solid rgba(255,255,255,.5);
top: 40%;
}
.custom-arrow::after {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-top: var(--size) solid rgba(255,255,255,.5);
 top: 60%;
}
.cbSubmitButtonContainer .cbSubmitButton {
background-color: #4d5061;
box-shadow: 0 10px 20px rgba(0,0,0,1);
}
.cbSubmitButtonContainer .cbSubmitButton:hover {
background-color: #4A65FF;
}`;
document.querySelector('head').appendChild(style);
}

const mainFunction = _ => {
hideSelectContainers()
addVirtualSelectContainer()
addVirtualSelectOptionsContainerListeners()
addStyles()
}


document.addEventListener('DataPageReady', _=>{


mainFunction()



})
</script>

 

Link to comment
Share on other sites

Hello Volomeister.

I have checked the links I sent you on several computers and it works everytime.

I have updated the code with your new one. seems that it works much better.

anyway, is it possible to somehow update the code, that the design would match the design of my website a lot more. now it has different color, also some margins and paddings seems to be little bit bigger.

Is it possible to reduce the code somehow only for having the text in more than one line?

 

I am going to test it also for bulk edit window, if it works or not. will let you know.

Link to comment
Share on other sites

Please try again the both links. I have sent the links to some of my friends, they tried it on their side, and it worked.

also I have made another datapage, where there is tabular report, and bulk edit.

I have tried to put your code in header of the tabular report, and also in bulk edit window, and then in both, but it seems that the code does not work for bulk edit window.

please try to open this link:

http://www.spravaregistratury.sk/index.php/test3/

and for id please use the same 845099866

 

Link to comment
Share on other sites

Hi @Lukas

Indeed, the code snippet above is not applicable for bulk editing on tabular report datapage.

You can add this one instead in the footer of the page where you configure results page fields:
 

<script>

const changeHandler = e => {

  let selectElement = e.target;
  let selectElementValue = selectElement.value
  let virtualSelectContainer = document.querySelector(`[virtual-attribute="${selectElement.getAttribute('id')}"]`)
  if(selectElementValue=='') {
  selectElementValue+= selectElement.querySelector('option[value=""]').innerText
  }
  virtualSelectContainer.querySelector('.virtualSelectText').innerText = selectElementValue;
  virtualSelectContainer.querySelector('.virtualOptionContainer').innerHTML = virtualSelectOptionsGenerator(selectElement).template
  
}


const hideSelectContainers = _=> {
document.querySelectorAll('.cbBulkFormSelect').forEach(input => {
input.style = "display: none;";
input.addEventListener('change', changeHandler)
})
}

const virtualSelectOptionsGenerator = selectElement => { 
    let template = '';
    selectElement.querySelectorAll('option').forEach(option=>{template += `<div value="${option.getAttribute('value')}" class="virtualOption" onclick="virtualOptionClickHandler(this)">${option.innerHTML}</div>`; option.classList.add("hideElement")})
    return {
            template,
            selectElement
    }
}


const virtualSelectContainerGenerator = optionsObj => {

  return `<div class="virtualSelectContainer" virtual-attribute="${optionsObj.selectElement.getAttribute('id')}"> 
               <div class="virtualSelect">
                <span class="virtualSelectText">${optionsObj.selectElement.value}</span>
                <div class="virtualOptionContainer hideElement">${optionsObj.template}</div>
               </div><span class="custom-arrow"></span></div>`;
}

const addVirtualSelectContainer = _=> {
document.querySelectorAll('[class*="cbFormBlock"]').forEach( item =>{
if(item.querySelector('select')!=null) {
      if(document.querySelector(`[virtual-attribute="${item.querySelector('select').getAttribute('id')}"]`) == null) {
      item.insertAdjacentHTML('beforeend', virtualSelectContainerGenerator(virtualSelectOptionsGenerator(item.querySelector('select'))))
      
      }
}
})
}

const updateVirtualSelectOptions = virtualSelectContainer => {
  
const selectElement = document.querySelector(`#${virtualSelectContainer.getAttribute('virtual-attribute')}`);
virtualSelectContainer.querySelector('.virtualOptionContainer').innerHTML = virtualSelectOptionsGenerator(selectElement).template;
virtualSelectContainer.querySelector('.virtualSelectText').innerText = selectElement.value;
addVirtualSelectOptionsContainerListeners()
}


const addVirtualSelectOptionsListeners = _=> {
  
document.querySelectorAll('.virtualOption').forEach( option => {
option.addEventListener('click', e=> {
    let realOption = document.querySelector(`[value="${e.target.getAttribute('value')}"]`);
    realOption.parentElement.selectedIndex = realOption.index 
    e.target.parentElement.parentElement.querySelector('.virtualSelectText').innerText = e.target.innerText;
    e.target.parentElement.classList.toggle("hideElement");
    realOption.dispatchEvent(new Event('change'))

  })
})
}



const virtualOptionClickHandler = function(e) {
  
    let realOption = document.querySelector(`[value="${e.getAttribute('value')}"]`);
    realOption.parentElement.selectedIndex = realOption.index 
    e.parentElement.parentElement.querySelector('.virtualSelectText').innerText = e.innerText;
    e.parentElement.classList.toggle("hideElement");
    realOption.parentElement.dispatchEvent(new Event('change'))
}

const addVirtualSelectOptionsContainerListeners = _=> {
 document.querySelectorAll('.virtualSelectContainer').forEach( item =>{
item.addEventListener('click', e => {
  let clickedElement = e.target;
  if (clickedElement.classList.contains('virtualSelectContainer')) {
  clickedElement.querySelector('.virtualOptionContainer').classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('virtualSelectText')) {
  clickedElement.nextElementSibling.classList.toggle('hideElement');
  }
  else if (clickedElement.classList.contains('custom-arrow')) {
  clickedElement.previousElementSibling.classList.toggle('hideElement');
  }
 }, true)
})
}

const addStyles = _=> {

let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
.virtualSelectContainer {
position: relative;
max-width: 800px;
--custom-paddings: 0.8em 4.5em 0.8em 1.1em;
--bg-color: #2B85BA;
--bg-color-hover: #0D3692;
box-sizing: border-box;
color: white;
white-space: normal;
}
.virtualSelectContainer:hover {
cursor: pointer;
}
.virtualSelect {
font-size: 0.8rem;
background: var(--bg-color);
border: 0;
max-width: 100%;
}
.hideElement {
display: none;
}
.virtualOption {
padding: var(--custom-paddings);
margin-top: 10px;
overflow: hidden;

}
.virtualOption:hover {
  background: var(--bg-color-hover);
  color: white;
}
.virtualSelectText {
display: block;
padding: var(--custom-paddings);
}
.custom-arrow {
  display: block;
  background-color: #965600;
  height: 100%;
  width: 5rem;
  position: absolute;
  right: 0;
  top: 0;
  pointer-events: none;
}
.custom-arrow::before, .custom-arrow::after {
  z-index: 10;
  --size: .65em;
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  left: 50%;
  transform: translate(-50%, -50%);
}
.custom-arrow::before {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-bottom: var(--size) solid rgba(255,255,255,.5);
top: 40%;
}
.custom-arrow::after {
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-top: var(--size) solid rgba(255,255,255,.5);
 top: 60%;
}
.cbSubmitButtonContainer .cbSubmitButton {
background-color: var(--bg-color);
}
.cbSubmitButtonContainer .cbSubmitButton:hover {
background-color: var(--bg-color-hover);
}`;
  
document.querySelector('head').appendChild(style);
}

const mainFunction = _ => {
hideSelectContainers()
addVirtualSelectContainer()
addVirtualSelectOptionsContainerListeners()
addStyles()
}



new MutationObserver(records=>{
    for (let i=0; i<records.length-1;i++) {
        if(records[i].addedNodes.length > 0) {
          if(typeof(records[i].addedNodes[0].getAttribute) == 'function') {
            if(records[i].addedNodes[0].getAttribute('id') == 'BulkUpdateForm') {
            mainFunction();
            break;
        }}
      }}
}).observe(document.querySelector('body'), { attributes: true, childList: true, subtree: true })

</script>

you can test it here: https://c7eku786.caspio.com/dp/7f80b0009cbe5678cbee40d0ae26

I also made a slight changes to the styling to it matches your current color scheme. 

Link to comment
Share on other sites

Hello Volomeister.

again - thank you very much for helping me with this one. I understand very little of css, so I made some changes to your code (only changed some colours), but there are still some things I am not able to do:

in the picture below:

on the right side - there is a datapage without your code, when I want to pick an option from the dropdown, I am able to see all (or almost all) of the options.

on the left side - its the same datapage with your code (I already did some colour change), but when I want to pick from the dropdown option, i am not able to see all of them but only one or two lines and then I need to scroll down, Aalso the letters are a little small.

 

image.thumb.png.f5bf2ecfb9b3adcb0ff6148467bea6e2.png

 

in this picture bellow you can see, that when I first open the bukl edit window, from the first dropdown, i am not able to see the whole option, it is visible only after I select one of the options.

 

 

image.thumb.png.43eea071fa5120e5c645fca2352d15e4.png

 

in this third picture, you can see that the selected option (blue one) shows ok, but for example the one above the selected option is somehow not shown ok, and can not really read the right side of all the lines.

also the gap between the options is quite big.

 

 

image.thumb.png.3aee63dad6562552f0b58096a93dfa23.png

how could I change all that? 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...