Trying and failing to make a linux terminal

核能气质少年 提交于 2021-01-27 15:40:37

问题


This may be a stupid question that might be easy to find but i'm quite new to all of this and i can't seem to find what i'm looking for or atleast i don't know what i need to look for, thus I'm here.

So what I'm trying to do is create a kind of Linux terminal... This is what i got so far.

What I'm stuck on is the actual entering text part... I've been trying to create a div with contenteditable=true as well as trying out Input elements but neither seems to be working how i want it to. The current structure that i'm using for this is:

<div class="title" contenteditable="false" >
admin@localhost:~$ 
<div class="write-point" contenteditable="true" ></div>
<div class="linux-cursor" contenteditable="false"></div>

However this only deletes the whole line of text. "admin@localhost:~$" as well as the cursor.

I've also tried using JavaScript to put the cursor after the text but its not working at all.

function forStackOverFlow() {
var textInput = document.getElementsByClassName('write-point');

textInput.onkeydown = function(e) {
    console.log(textInput.value);
    var childTag = document.getElementsByClassName("write-point");
    childTag.parentNode.insertBefore(textInput.value, childTag.nextSibling);
}};

So my main questions are:

  • How and what is needed to move a div(cursor element) to the end of input text(user input)
  • Is it possible to allow a user to type immediately once the webpage has loaded?

Thanks, any help would be great :)


回答1:


I suggest to use span instead of div because it's an inline element, more easy to manage in your case.

Next you can catch eatch keyboard's entry with a listener :

document.body.onkeydown

and tell him to append each key into a variable that you can display.

I let you think about all the functionnality you have to manage, like an enter or a backspace event.

you can play and find code you will need here : http://keycode.info/

Here is a working snippet :

var command = "";

document.body.onkeydown = function(e){
    var writePoint = document.getElementById('writePoint');
    command += String.fromCharCode(e.keyCode);
    writePoint.innerHTML = command;
  };
.linux-cursor{
  width:7px;
  height:15px;
  background-color:green;
  display:inline-block;
}
<span class="title" contenteditable="false" >
  admin@localhost:~$ 
</span>
<span class="write-point" id="writePoint" contenteditable="true" ></span>
<span class="linux-cursor" contenteditable="false"></span>

Hope this help you, it looks like a nice project.




回答2:


You can do that in a better way with some CSS to make sure the "caret" element always comes after the contenteditable one and some JS to make sure the contenteditable element is always focused. You might try to do this last thing by adding autofocus to the contenteditable element and using a <label> for the caret element, but that doesn't work on contenteditable elements. Note no keyboard event listeners are needed:

const input = document.getElementById('input');
const caret = document.getElementById('caret');

// Move the focus back to the input if it moves away from it:
input.addEventListener('blur', (e) => {  
  input.focus();
});

// Set the focus to the input so that you can start typing straight away:
input.focus();
body {
  background: #000;
  color: #0F0;
  font-family: monospace;
  height: 100vh;
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: scroll;
  margin: 0;
  padding: 16px;
}

#input {
  display: inline;
  word-break: break-all;
  outline: none;
  visibility: visible;
}

#caret {
  border: 0;
  padding: 0;
  outline: none;
  background-color: #0F0;
  display: inline-block;
  font-family: monospace;
}
admin@localhost:~$

<div id="input" contenteditable="true"></div><button id="caret" for="input">&nbsp;</button>

In a more realistic example, you might want to:

  • Avoid trapping the focus in the contenteditable element, as that would prevent selecting previous commands. Instead, focus to the contenteditable element only once the user presses some key.

  • Show a different caret depending on its position: square if it's at the end of the input, line if it's somewhere else (unless overtype mode is enabled with the Ins key).

  • Add a new command/entry if is pressed.

  • Prevent entering formatted text and automatically split it up into multiple commands/entries when needed.

const history = document.getElementById('history');
const input = document.getElementById('input');
const cursor = document.getElementById('cursor');

function focusAndMoveCursorToTheEnd(e) {  
  input.focus();
  
  const range = document.createRange();
  const selection = window.getSelection();
  const { childNodes } = input;
  const lastChildNode = childNodes && childNodes.length - 1;
  
  range.selectNodeContents(lastChildNode === -1 ? input : childNodes[lastChildNode]);
  range.collapse(false);

  selection.removeAllRanges();
  selection.addRange(range);
}

function handleCommand(command) {
  const line = document.createElement('DIV');
  
  line.textContent = `admin@localhost:~$ ${ command }`;
  
  history.appendChild(line);
}

// Every time the selection changes, add or remove the .noCursor
// class to show or hide, respectively, the bug square cursor.
// Note this function could also be used to enforce showing always
// a big square cursor by always selecting 1 chracter from the current
// cursor position, unless it's already at the end, in which case the
// #cursor element should be displayed instead.
document.addEventListener('selectionchange', () => {
  if (document.activeElement.id !== 'input') return;
  
  const range = window.getSelection().getRangeAt(0);
  const start = range.startOffset;
  const end = range.endOffset;
  const length = input.textContent.length;
  
  if (end < length) {
    input.classList.add('noCaret');
  } else {
    input.classList.remove('noCaret');
  }
});

input.addEventListener('input', () => {    
  // If we paste HTML, format it as plain text and break it up
  // input individual lines/commands:
  if (input.childElementCount > 0) {
    const lines = input.innerText.replace(/\n$/, '').split('\n');
    const lastLine = lines[lines.length - 1];
    
    for (let i = 0; i <= lines.length - 2; ++i) {
      handleCommand(lines[i]);
    }
  
    input.textContent = lastLine;
    
    focusAndMoveCursorToTheEnd();
  }
  
  // If we delete everything, display the square caret again:
  if (input.innerText.length === 0) {
    input.classList.remove('noCaret');  
  }  
});

document.addEventListener('keydown', (e) => {   
  // If some key is pressed outside the input, focus it and move the cursor
  // to the end:
  if (e.target !== input) focusAndMoveCursorToTheEnd();
});

input.addEventListener('keydown', (e) => {    
  if (e.key === 'Enter') {
    e.preventDefault();
        
    handleCommand(input.textContent);    
    input.textContent = '';
    focusAndMoveCursorToTheEnd();
  }
});

// Set the focus to the input so that you can start typing straigh away:
input.focus();
body {
  background: #000;
  color: #0F0;
  font-family: monospace;
  height: 100vh;
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: scroll;
  word-break: break-all;
  margin: 0;
  padding: 16px;
}

#input {
  display: inline;
  outline: none;
  visibility: visible;
}

/*
  If you press the Insert key, the vertical line caret will automatically
  be replaced by a one-character selection.
*/
#input::selection {
  color: #000;
  background: #0F0;
}

#input:empty::before {
  content: ' ';
}

@keyframes blink {
  to {
    visibility: hidden;
  }
}

#input:focus + #caret {
  animation: blink 1s steps(5, start) infinite;
}

#input.noCaret + #caret {
  visibility: hidden;
}

#caret {
  border: 0;
  padding: 0;
  outline: none;
  background-color: #0F0;
  display: inline-block;
  font-family: monospace;
}
<div id="history"></div>

admin@localhost:~$

<div id="input" contenteditable="true"></div><button id="caret" for="input">&nbsp;</button>

In general, it's usually a bad idea to listen for keyboard events (keydown / keypress / keyup) to handle text input or cursors, as the value of the input can also be updated by pasting or dropping text into it and there are many edge cases, such as arrows, delete, escape, shortcuts such as select all, copy, paste... so trying to come up with an exhaustive list of all the keys we should take care of is probably not the best approach.

Moreover, that won't work on mobile, where most keys emit the same values e.key = 'Unidentified', e.which== 229 and e.keyCode = 229.

Instead, it's usually better to rely on other events such as input and use KeyboardEvents to handle very specific keys, like in this case.

If you need to check KeyboardEvent's properties values such as e.key, e.code, e.which or e.keyCode you can use https://keyjs.dev. I will add information about these kinds of cross-browser incompatibilities soon!

Disclaimer: I'm the author.



来源:https://stackoverflow.com/questions/47459914/trying-and-failing-to-make-a-linux-terminal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!