1

I am attempting to display two new buttons after clicking the first one. To do this I have the two buttons I want to display set to display: none. When the initial button is clicked it toggles its display to none and then displays the other two buttons as display: block.

This works but a weird display bug occurs where initially the buttons are much larger than they should be, and quickly return to the size they are supposed to be.

I've attached images to show this, but could not embed because my reputation.

Screenshot 1 enter image description here

Screenshot 2 enter image description here

Screenshot 3 enter image description here

HTML

<div class = "container">
   <div class = "row p-0"> 
      <div class = "col text-field-wrapper m-0 p-0"></div>
      <div class = "col button-field-wrapper"></div>
   </div>
</div>

CSS:

.button-field-wrapper {
    font-size: 12px; 
    margin: 18px 0 0 0; 
    height: 36px; 
    display: block;
}
.hide {
    display: none
}
.text-field-wrapper: {
    height: 56px; 
    display: block;
}

JavaScript:

constructor() {
    super();
    this.active = false;
    this.default = true;
    this.maxWidth = 200;
    this.error = false; 
    this.overflow = false; 
    this.getWidth();

    this.inputText = "Enter A Title";
    this.appendChild(template.content.cloneNode(true));


    this.tf_wrapper = this.getElementsByClassName("text-field-wrapper")[0];
    this.bt_wrapper = this.getElementsByClassName("button-field-wrapper")[0];

    this.renderInputField.bind(this)(this.tf_wrapper);
    this.renderButtonField.bind(this)(this.bt_wrapper);
    this.renderOptionField.bind(this)(this.bt_wrapper);
    this.optionWrapper.setAttribute("class", "hide", "option-wrapper")


} 

renderOptionField(bt_wrapper) {
    this.optionWrapper = document.createElement("DIV");
    this.optionWrapper.setAttribute("class", "option-wrapper");
    bt_wrapper.appendChild(this.optionWrapper);

    this.cancelButton = document.createElement("Button");
    this.cancelButton.setAttribute("class", "cancel-button");
    this.cancelButton.addEventListener("click", 
    this.onCancel.bind(this))

    this.closeIcon = document.createElement("object");
    this.closeIcon.setAttribute("type", "image/svg+xml");
    this.closeIcon.setAttribute("class", "close-icon");
    this.closeIcon.setAttribute("data", "Close.svg")
    this.cancelButton.appendChild(this.closeIcon)
    this.cancelButton.innerHTML += "Cancel";

    this.saveButton = document.createElement("Button");
    this.saveButton.setAttribute("class", "save-button");
    this.saveButton.addEventListener("click", this.onSave.bind(this))
    this.saveIcon = document.createElement("object");
    this.saveIcon.setAttribute("type", "image/svg+xml");
    this.saveIcon.setAttribute("class", "save-icon");
    this.saveIcon.setAttribute("data", "Check.svg")
    this.saveButton.appendChild(this.saveIcon)
    this.saveButton.innerHTML += "Save";
    this.optionWrapper.appendChild(this.cancelButton);
    this.optionWrapper.appendChild(this.saveButton);        
}

onEdit() {
    this.editWrapper.setAttribute("class", "edit-wrapper hide")
    this.optionWrapper.setAttribute("class", "option-wrapper")
    this.textField.setAttribute("class", "text-field-active single- 
    line")
    this.active = true;
}
Supersharp
  • 29,002
  • 9
  • 92
  • 134
Kevin A
  • 13
  • 3

2 Answers2

1

This works but a weird display bug occurs where initially the buttons are much larger than they should be, and quickly return to the size they are supposed to be.

Even though your items are completly created when you append them to your container, your browser needs to recalculate your flow, to make elements fit inside. While doing this, your items may initially not have correct values set, which can result in strange behaviors sometimes.

Creating elements like this isn’t an efficient way to go. You’d better write your HTML with everything inside, including elements you want to show and those you want to hide, and change their properties on the go. You’ll avoid long, hardly maintainable javascript code, while getting more stable behavior.

Here’s a fiddle that kinda reproduce what you are trying to achieve :

// Script.js

// Load all the elements you need to operate on
const container = document.querySelector('#container');
const staticElements = container.querySelectorAll('.static');
const editElements = container.querySelectorAll('.edit');
const inputField = container.querySelector('input');
const title = container.querySelector('.title');

// Hide all static elements, and display the edit ones
function setEditMode() {
  inputField.value = title.innerHTML;

  for (const element of staticElements) {
    element.classList.add('hidden');
  }
  
  for (const element of editElements) {
    element.classList.remove('hidden');
  }
}

// Reverse of above function
function setStaticMode() {
  for (const element of staticElements) {
    element.classList.remove('hidden');
  }
  
  for (const element of editElements) {
    element.classList.add('hidden');
  }
}

// Call above, but before update static value
function save() {
  title.innerHTML = inputField.value;
  setStaticMode();
}
/* style.css */

#container {
  display: flex;
}

#container > * {
  margin: 5px;
}

.editable {
  font-size: 20px;
  color: #AAAAAA;
}

.button {
  border-style: solid;
  border-color: lightblue;
  border-width: 1px;
  border-radius: 3px;
  padding: 3px;
  cursor: pointer;
}

.editActions {
  display: flex;
}

.editActions > * {
  margin: 0 5px;
}

.hidden {
  display: none;
}
<!—-index.html—->

<div id="container">
  <div class="title static">Enter a title</div>
  <input type="text" class="hidden edit">
  <div class="button static" onClick="setEditMode()">
    edit
  </div>
  <div class="hidden editActions edit">
    <div class="button" onClick="setStaticMode()">
      cancel
    </div>
    <div class="button" onCLick="save()">
      save
    </div>
  </div>
</div>

See, here, we do not modify the DOM from javascript, and everything looks clear, behaving as expected.

If you have to build HTML from javascript, do it carefully, and as occasionally as possible. You can go from here and adapt your code, to see if the changes are effective.

KawaLo
  • 1,783
  • 3
  • 16
  • 34
  • Awesome thanks so much. I also was able to fix the issue by using image for the svg instead of object. The problem seemed to be coming from the icons I was attempting to add to the buttons, as without them it loaded correctly. Thanks for the advice tho about using as much HTML as I can, I will definitely use that in the future. – Kevin A Jul 18 '19 at 18:16
0

This is one of the reasons it is advised to build your web application for static pages (with no JavaScript) first and then scale up the functionality to incorporate JavaScript for dynamism.

For instance, if you adhere to the above advice then the browser's html parser would have computed the properties for all your DOM containers first before you apply any effect via JavaScript. If you don't do this, then this kind of unpredictable behavior is expected.

Specifically, from your code, there is no effort to make sure that the DOM was ready before you start appending to or manipulating it's elements. See this post on how to achieve this using vanilla JavaScript. Once you've made sure the DOM is ready, you can then hide elements you want to display dynamically using JavaScript and add event listeners you want to use to make them visible.

Udo E.
  • 2,665
  • 2
  • 21
  • 33