junkerstock
 簡易的な計算機10 履歴M 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>メモリ履歴電卓</title>
<style>
/* 基本設定 */
* {
box-sizing: border-box;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
body {
margin: 0;
padding: 0;
height: 100dvh;
width: 100vw;
display: flex;
flex-direction: column;
background-color: #000;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
overflow: hidden;
}

/* 画面上部エリア */
#display-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
padding-bottom: 10px;
overflow: hidden;
}

/* 履歴表示エリア */
#history {
width: 100%;
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
color: #888;
font-size: 0.9rem;
line-height: 1.4;
padding-bottom: 5px;
margin-bottom: 5px;
border-bottom: 1px solid #333;
}

.history-item {
font-family: monospace;
display: flex;
align-items: center;
gap: 10px; /* 計算式とMタグの隙間 */
}

/* 履歴につくMタグのデザイン */
.m-tag {
color: #FFD60A; /* 黄色 */
font-weight: bold;
}

/* メイン数値表示部分 */
#display {
width: 100%;
color: white;
font-size: 3.5rem;
font-weight: 300;
text-align: right;
word-break: break-all;
line-height: 1.1;
max-height: calc(3.5rem * 1.1 * 2);
overflow-y: auto;
flex-shrink: 0;
}

#display::-webkit-scrollbar, #history::-webkit-scrollbar {
display: none;
}

/* ボタンエリア */
.buttons {
height: 55%;
padding-bottom: env(safe-area-inset-bottom);
margin-bottom: 10px;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 8px;
padding-left: 15px;
padding-right: 15px;
}

button {
border: none;
font-size: 1.7rem;
border-radius: 1000px;
cursor: pointer;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.1s;
height: 100%;
}

button:active {
filter: brightness(1.2);
transform: scale(0.98);
}

.number { background-color: #333333; }
.func { background-color: #a5a5a5; color: black; font-weight: 500; }
.time-btn { background-color: #a5a5a5; color: black; font-size: 1.2rem; font-weight: bold; }

/* Mキーのデザイン */
.m-key {
background-color: #FFD60A; /* 黄色 */
color: black;
font-weight: bold;
}

/* Eモードになったときの=キーのデザイン */
.execute-mode {
background-color: #30d158 !important; /* 緑色 */
font-weight: bold;
}

/* 記号ボタンの色指定 (順番調整済み) */
.buttons button:nth-child(4) { background-color: #FF3B30; color: white; } /* ÷ */
.buttons button:nth-child(8) { background-color: #FF2D55; color: white; } /* × */
.buttons button:nth-child(12) { background-color: #AF52DE; color: white; } /* - */
.buttons button:nth-child(16) { background-color: #5856D6; color: white; } /* + */

/* =キー (通常時:青) */
#btn-equal {
background-color: #007AFF;
color: white;
}

</style>
</head>
<body>

<div id="display-container">
<div id="history"></div>
<div id="display">0</div>
</div>

<div class="buttons">
<button class="func" onclick="clearDisplay()">C</button>
<button class="func" onclick="deleteLast()">←</button>
<button class="time-btn" id="btn-time" onclick="cycleTime()">分</button>
<button class="operator" onclick="appendDisplay('/')">÷</button>

<button class="number" onclick="appendDisplay('7')">7</button>
<button class="number" onclick="appendDisplay('8')">8</button>
<button class="number" onclick="appendDisplay('9')">9</button>
<button class="operator" onclick="appendDisplay('*')">×</button>

<button class="number" onclick="appendDisplay('4')">4</button>
<button class="number" onclick="appendDisplay('5')">5</button>
<button class="number" onclick="appendDisplay('6')">6</button>
<button class="operator" onclick="appendDisplay('-')">−</button>

<button class="number" onclick="appendDisplay('1')">1</button>
<button class="number" onclick="appendDisplay('2')">2</button>
<button class="number" onclick="appendDisplay('3')">3</button>
<button class="operator" onclick="appendDisplay('+')">+</button>

<button class="m-key" id="btn-m" onclick="toggleMemoryMode()">M</button>
<button class="number" onclick="appendDisplay('0')">0</button>
<button class="number" onclick="appendDisplay('.')">.</button>
<button class="operator" id="btn-equal" onclick="handleEqual()">=</button>
</div>

<script>
const display = document.getElementById('display');
const historyDiv = document.getElementById('history');
const btnTime = document.getElementById('btn-time');
const btnEqual = document.getElementById('btn-equal');
const btnM = document.getElementById('btn-m');

const operators = ['/', '*', '-', '+'];
const unitNames = ["分", "時間", "日", "年", "秒"];
let timeState = 0;

// 履歴管理用の変数
let historyCount = 0;
let historyData = {}; // { 1: "2", 2: "10" } のように結果を保存

// メモリモード管理用の変数
let isMemoryMode = false;
let memoryInput = ""; // Mの後に押された数字を一時保存

function getDisplayValue() {
return display.innerText;
}

function setDisplayValue(val) {
display.innerText = val;
}

// 履歴追加ロジック
function addHistory(text, result) {
historyCount++;
// 結果を保存
historyData[historyCount] = result;

const item = document.createElement('div');
item.className = 'history-item';

// 計算式と、黄色いMタグを表示
item.innerHTML = `<span>${text} = ${result}</span> <span class="m-tag">M${historyCount}</span>`;

historyDiv.appendChild(item);
historyDiv.scrollTop = historyDiv.scrollHeight;
}

// 数字や記号が押されたときの処理(Mモードかどうかで分岐)
function appendDisplay(input) {
// ■ メモリモード中 ■
if (isMemoryMode) {
// 数字のみ受け付ける
if (!operators.includes(input) && input !== '.') {
memoryInput += input;
// Eボタンの上に、いま何番を入力しているか表示してあげると親切
btnEqual.innerText = "E(" + memoryInput + ")";
}
return;
}

// ■ 通常モード ■
const currentVal = getDisplayValue();
const lastChar = currentVal.slice(-1);

if (currentVal === '0' && !operators.includes(input) && input !== '.') {
setDisplayValue(input);
return;
}

if (operators.includes(input) && operators.includes(lastChar)) {
return;
}

setDisplayValue(currentVal + input);
}

// Mボタンが押されたとき
function toggleMemoryMode() {
// モード開始
isMemoryMode = true;
memoryInput = ""; // バッファクリア

// ボタンの見た目を変える
btnEqual.innerText = "E";
btnEqual.classList.add("execute-mode"); // 緑色にするクラス
btnM.style.filter = "brightness(0.8)"; // Mボタンを押してる感
}

// =(またはE)ボタンが押されたとき
function handleEqual() {
// ■ Eキーとして動作する場合 ■
if (isMemoryMode) {
// メモリ呼び出し処理
const targetIndex = parseInt(memoryInput);

if (historyData[targetIndex] !== undefined) {
// 該当する履歴があれば、画面に追加
// メモリモード終了処理
endMemoryMode();

// 現在の画面の末尾が記号でなければ、掛け算などを補うべきか?
// 今回はシンプルに「文字として追記」します(ユーザーが + を押してから呼ぶ想定)
const currentVal = getDisplayValue();
if (currentVal === "0") {
setDisplayValue(historyData[targetIndex]);
} else {
setDisplayValue(currentVal + historyData[targetIndex]);
}

} else {
// 履歴がない場合
endMemoryMode();
// エラー表示等はせず、何もしない(あるいはボタンを一瞬赤くするなど)
}
return;
}

// ■ 通常の=キーとして動作する場合 ■
calculateResult();
}

function endMemoryMode() {
isMemoryMode = false;
memoryInput = "";
btnEqual.innerText = "=";
btnEqual.classList.remove("execute-mode");
btnM.style.filter = "none";
}

function clearDisplay() {
if (isMemoryMode) {
endMemoryMode(); // Mモード中にCを押したらモード解除
} else {
setDisplayValue("0");
}
}

function deleteLast() {
if (isMemoryMode) {
// Mモード中は入力中のメモリ番号を消す
memoryInput = memoryInput.slice(0, -1);
if(memoryInput === "") {
btnEqual.innerText = "E";
} else {
btnEqual.innerText = "E(" + memoryInput + ")";
}
return;
}

const currentVal = getDisplayValue();
if (currentVal.length > 1) {
setDisplayValue(currentVal.slice(0, -1));
} else {
setDisplayValue("0");
}
}

// 時間変換(以前のまま)
function cycleTime() {
if (isMemoryMode) return; // Mモード中は無効

try {
let val = parseFloat(eval(getDisplayValue()));

if (timeState === 0) val = val / 60;
else if (timeState === 1) val = val / 24;
else if (timeState === 2) val = val / 365;
else if (timeState === 3) val = val * 365 * 24 * 60 * 60;
else if (timeState === 4) val = val / 60;

timeState++;
if (timeState >= unitNames.length) timeState = 0;

setDisplayValue(val);
updateUnitDisplay();

} catch (e) {
setDisplayValue("Error");
}
}

function updateUnitDisplay() {
btnTime.innerText = unitNames[timeState];
}

// 計算実行ロジック
function calculateResult() {
try {
let expression = getDisplayValue();
let result = new Function('return ' + expression)();

// 履歴に追加 (式 = 答え M番号)
// 同じ計算でも履歴に残す仕様にします(変数の値が変わるわけではないので)
addHistory(expression, result);

setDisplayValue(result);
} catch (error) {
setDisplayValue("Error");
}
}
</script>
</body>
</html>


使用変数

addHistory -------( Function )
appendDisplay -------( Function )
btnEqual
btnM
btnTime
calculateResult -------( Function )
charset
class
className
clearDisplay -------( Function )
content
currentVal
cycleTime -------( Function )
deleteLast -------( Function )
display
endMemoryMode -------( Function )
expression
filter
fit
getDisplayValue -------( Function )
handleEqual -------( Function )
historyCount
historyData
historyDiv
id
innerHTML
innerText
isMemoryMode
item
lang
lastChar
memoryInput
name
onclick
operators
result
scalable
scale
scrollTop
setDisplayValue -------( Function )
targetIndex
timeState
toggleMemoryMode -------( Function )
unitNames
updateUnitDisplay -------( Function )
val
width