БлогNot. Полезные мини-скрипты на JavaScript

Полезные мини-скрипты на JavaScript

сейчас в списке: 33 скрипта Этот список будет включать в себя небольшие задачи на JavaScript, поменьше объёмом, чем вот эти, и, в общем, представляющие собой законченные мини-приложения, если вставить код в тело любого документа HTML5 в кодировке Юникода UTF-8.

Полезные алгоритмы есть также в этой заметке (с JS-массивами), на сайте nickolay.info в разделе по JavaScript и просто по соответствующему тегу блога.

Список будет пополняться, для быстрого поиска на странице нужного слова используйте комбинацию клавиш Ctrl+F в браузере.

1. Разметить строку посимвольно случайными тегами из разрешённого набора, аналог текстовой капчи
<div id="captcha"></div>
<script>
function rndFormat (text) {
 let tags = [
  '<i>',  '<b>',  '<u>',  '<sup>',  '<sub>',  '<big>',  '<small>'
 ];
 let result = '';
 for (let i=0; i<text.length; i++) {
  let num=Math.floor(Math.random()*tags.length);
  result += tags[num] + text[i] + '</' + tags[num].substring(1);
 }
 return result;
}
document.getElementById('captcha').innerHTML = rndFormat('Hello!');
</script> 
2. Проверить корректность даты

Проще всего задействовать стандартный объект Date.

<script>
function validate_date (id) {
 let arrD = document.getElementById(id).value.split('.');
 arrD[1]--;//у обьекта date отсчет месяцев начинается с нуля 
 let dt = new Date(arrD[0], arrD[1], arrD[2]); //встроенный обьект
 return (
  (dt.getFullYear() == arrD[0]) && (dt.getMonth() == arrD[1]) && (dt.getDate() == arrD[2]) 
   ? true : false);
}
</script>
<input type="text" id="dt0" size="25" maxlength="10" placeholder="дата в формате ГГГГ.ММ.ДД">
<input type="button" value="Проверить" 
 onclick="document.getElementById('dateResult').innerHTML = validate_date('dt0');">
<span id="dateResult"></span>
3. Найти день недели по формуле Зеллера

Здесь мы получаем день недели не из объекта, а считаем по формуле. Без parseInt в функции будет считать неправильно :)

Функция pad дополняет слева натуральное число n до size разрядов нулями.

<script>
function weekday (id) {
 let arrD = document.getElementById(id).value.split('.');
 let y = parseInt(arrD[0]), m = parseInt(arrD[1]), d = parseInt(arrD[2]), dday = -1;
 //корректность даты не проверяется!
 if (m<3) { m+=12; y--; }
 let n = y%100, c = Math.floor(y/100);
 dday= (Math.floor((m+1)*26/10)+d+n+Math.floor(n/4)+Math.floor(c/4)-2*c) % 7;
 let wday = new Array ('Сб','Вс','Пн','Вт','Ср','Чт','Пт');
 function pad (n,size) { return String(Array(size-1).fill('0').join('')+n).slice(-size); }
 return pad(arrD[2],2)+'.'+pad(arrD[1],2)+'.'+pad(arrD[0],4)+'='+wday[dday];
}
</script>
<input type="text" id="dt0" size="25" maxlength="10" placeholder="дата в формате ГГГГ.ММ.ДД">
<input type="button" value="Вычислить" 
 onclick="document.getElementById('dateResult').innerHTML = weekday('dt0');">
<span id="dateResult"></span>
4. По заданным интенсивностям красного, зелёного и синего цветов получить строку вида rgb(23,100,134)

Дополнительно код показывает цвет фоном раздела rgbResult.

<script>
 function rgb (r0=0,g0=0,b0=0) {
  let r = Math.max(0,Math.min(r0,255)),
      g = Math.max(0,Math.min(g0,255)),
      b = Math.max(0,Math.min(b0,255));
  return 'rgb('+r+','+g+','+b+')';
 }
 function pad3 (i) { return String('00' + i).slice(-3); } //'0'=>'000', '10'=>'010'
</script>
<div id="rgbResult">&nbsp;</div>
<span id="red_sel" style="font-family: monospace; background-color: red;">128</span>
<input type="range" id="red_val" min="0" max="255" step="1" value="128"
 onchange="document.getElementById('red_sel').innerHTML = pad3(this.value);">
<span id="green_sel" style="font-family: monospace; background-color: green;">128</span>
<input type="range" id="green_val" min="0" max="255" step="1" value="128"
 onchange="document.getElementById('green_sel').innerHTML = pad3(this.value);">
<span id="blue_sel" style="font-family: monospace; background-color: #3333ff;">128</span>
<input type="range" id="blue_val" min="0" max="255" step="1" value="128"
 onchange="document.getElementById('blue_sel').innerHTML = pad3(this.value);">
<input type="button" value="Выполнить" 
  onclick="let c = rgb(document.getElementById('red_val').value,
                       document.getElementById('green_val').value,
                       document.getElementById('blue_val').value);
   document.getElementById('rgbResult').style.backgroundColor = c;
   document.getElementById('rgbString').innerHTML = c;">
<span id="rgbString" style="font-family: monospace;"></span>
5. Выбрать случайную строку из набора строк (случайный элемент массива)
<script>
function randomText() {
 let arr= [
 'Текст 1',
 'Текст 2',
 'Текст 3',
 'Текст 4',
 'Текст 5',
 'Текст 6',
 'Текст 7',
 'Текст 8',
 'Текст "9"',
 'Текст \'10\''
 ];
 return arr[Math.floor(Math.random()*arr.length)];
}
</script>
<div onclick="this.innerHTML=randomText();">Нажми меня!</div>
6. Поменять местами два значения в форме
<form>
 <input type="text" id="a" size="4" maxlength="3">
 <input type="text" id="b" size="4" maxlength="3">
 <input type="button" value="Поменять местами" onclick="f('a','b'); return false;">
</form>
<script>
 function f(id1,id2) {
  let a = document.getElementById(id1).value;
  let b = document.getElementById(id2).value;
  let temp = a; a = b; b = temp;
  document.getElementById(id1).value = a;
  document.getElementById(id2).value = b;
 }
</script>

Если значения должны быть целыми числами и обмен выполняется с проверкой и коррекцией данных, то так (обмен делается арифметически для разнообразия):

<form>
 <input type="text" id="a" size="4" maxlength="3">
 <input type="text" id="b" size="4" maxlength="3">
 <input type="button" value="OK" onclick="f('a','b'); return false;">
</form>
<script>
 function f(id1,id2) {
  let a = parseInt(document.getElementById(id1).value);
  if (isNaN(a)) a = 0; 
  let b = parseInt(document.getElementById(id2).value);
  if (isNaN(b)) b = 0; 
  a += b; b = a - b; a -= b;
  document.getElementById('a').value = a;
  document.getElementById('b').value = b;
 }
</script>
7. Табулировать произвольную функцию одного аргумента в заданных пределах

Пределы и функция передаются аргументами в tab.

<div id="table"></div>
<script>
 function tab (x1,dx,x2,f) {
  let s='<pre>'+"\n";
  for (let x=x1; x<=x2; x+=dx) {
   s += x.toFixed(3).toString().padStart(10)+' '+f(x).toFixed(3).toString().padStart(10)+"\n";
  }
  return s+'</pre>';
 }
 let f = function (x) {
  return Math.sin(x);
 }
 let div = document.getElementById('table');
 div.innerHTML = tab(0,Math.PI/10,2*Math.PI,f);
</script>
8. Заменить символы табуляции пробелами без изменения внешнего вида текста

Как известно, табуляция позволяет "перескочить" в текстовом редакторе на следующую позицию, кратную восьми (необязательно, но чаще всего). Если размер табуляции в вашем текстовом редакторе иной - текст может "разъехаться" далеко вправо и стать нечитабельным. В общем, бывает спокойнее, когда символа табуляции нет совсем, а выравнивание текста терять обычно не хочется (имеются в виду, конечно, редакторы неформатированного текста и с моноширинными шрифтами, которыми, как правило, программисты пишут листинги).

Код, показанный ниже, удаляет из элемента src символы табуляции, стараясь корректно заменить их пробелами, и помещает результат обработки в элемент res.

<script>
 function detab (text, tsize = 8) {
  let arr = text.split("\n");
  let res = '';
  for (let i = 0; i<arr.length; i++) {
   let str = arr[i], newstr = '';
   for (let k = 0; k < str.length; k++) {
    if (str[k] == '\t') {
     let newlen = newstr.length;
     for (let j = 0; j < tsize - newlen % tsize; j++) newstr += ' ';
    }
    else newstr += str[k];
   }
   res += newstr + (i<arr.length-1 ? '\n' : '');
  }
  return res;
 }
</script>
<textarea id="src" rows="5" cols="90" style="font-family: monospace;">
123	вторая позиция, в следующей строке одни табуляции
0	11	222	3333	4444	55555	666666	7777777	88888888	9
</textarea>
<br>
<textarea id="res"  rows="5" cols="90" style="font-family: monospace;"></textarea>
<br>
<button onclick="document.getElementById('res').value = detab (document.getElementById('src').value);">
ok</button>
9. Как "подогнать" размеры канвы под нужные пиксельные размеры рисунка?
<canvas id="canvas1" style="border: 1px dotted red;"></canvas>
<img id="image1" src="kar.jpg" style="visibility: hidden;">
<script>
 let canvas = document.getElementById('canvas1');
 let ctx = canvas.getContext('2d');
 let imageObj = new Image();
 imageObj.src = document.getElementById("image1").src;
 imageObj.onload = function () {
  canvas.width = imageObj.width;
  canvas.height = imageObj.height;
  ctx.drawImage(imageObj,0,0,imageObj.width,imageObj.height,0,0,canvas.width,canvas.height);
 };
</script>

Здесь оригинальный рисунок (с именем kar.jpg из текущей папки скрипта) скрыт стилем, а показан размещённый в канве.

Можно, конечно, и масштабировать рисунок по канве как здесь, и перестраивать изображение при масштабировании окна.

10. Как загрузить файл с рисунком на canvas? Как обработать файл на canvas попиксельно?

Для загрузки файла используется современный подход через FileReader. Рисунок загружается в верхнюю канву, а результат его попиксельной обработки фильтром - в нижнюю. В данном случае фильтрация функцией sepia сводится к наложению одноимённого эффекта.

<input type="file" id="imageLoader">
<br>
<canvas id="sourceCanvas"></canvas>
<br>
<canvas id="targetCanvas"></canvas>
<script>
 let imageLoader = document.getElementById('imageLoader');
 imageLoader.addEventListener('change', handleImage, false);
 let sourceCanvas = document.getElementById('sourceCanvas');
 let ctx = sourceCanvas.getContext('2d');
 let targetCanvas = document.getElementById('targetCanvas');

 function handleImage (e) {
  let reader = new FileReader();
  reader.onload = function (event) {
   let img = new Image();
   img.onload = function() {
    sourceCanvas.width = targetCanvas.width = img.width;
    sourceCanvas.height = targetCanvas.height = img.height;
    ctx.drawImage (img, 0, 0, img.width, img.height);
    let data = ctx.getImageData(0,0,img.width, img.height);
    sepia (data, img);
   }
   img.src = event.target.result;
  }
  reader.readAsDataURL (e.target.files[0]);     
 }

 function sepia (imageData, img) {
  let pixels = imageData.data;
  for (let i = 0; i < pixels.length; i += 4) {
   let r = pixels[i];
   let g = pixels[i + 1];
   let b = pixels[i + 2];
   pixels[i] = (r * 0.393)+(g * 0.769)+(b * 0.189);
   pixels[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168);
   pixels[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131);
  }
  let ctx = targetCanvas.getContext("2d");
  ctx.drawImage (img, 0, 0, img.width, img.heigth);
  ctx.putImageData (imageData, 0, 0);
 }
</script>
11. Как умножить 2 длинных натуральных числа?

В блоге есть подобное на C++, но компилировать-то обычно лень, пусть будет ещё на JS.

<script>
function mult(strNum1,strNum2){
 let a1 = strNum1.split("").reverse();
 let a2 = strNum2.toString().split("").reverse();
 let aResult = new Array();
 for ( let iterNum1 = 0; iterNum1 < a1.length; iterNum1++ ) {
  for ( let iterNum2 = 0; iterNum2 < a2.length; iterNum2++ ) {
   let idxIter = iterNum1 + iterNum2;
   aResult[idxIter] = a1[iterNum1] * a2[iterNum2] + ( idxIter >= aResult.length ? 0 : aResult[idxIter] );
   if ( aResult[idxIter] > 9 ) {
    aResult[idxIter + 1] = Math.floor( aResult[idxIter] / 10 ) + ( idxIter + 1 >= aResult.length ? 0 : aResult[idxIter + 1] );
    aResult[idxIter] %= 10;
   }
  }
 }
 return aResult.reverse().join("");
}
</script>
<input type="text" maxlength="99" size="100" id="num1">
<br>
<input type="text" maxlength="99" size="100" id="num2">
<br>
<input type="button" value="Умножить" 
 onclick="document.getElementById('res').innerHTML=
  mult(document.getElementById('num1').value,document.getElementById('num2').value);">
<div id="res"></div>
12. Сгенерировать случайный текст на основе последовательности фраз

Примерно как здесь, только проще и на стороне клиента.

<div id="textContent"></div>
<script>
function generateText (id, n) {
 let count = parseInt(n);
 if (isNaN(count) || count<1) count = 1;
 let w1 = new Array ("Товарищи,", "С другой стороны", "Равным образом", "Не следует однако забывать, что", 
  "Таким образом,", "Повседневная практика показывает, что", "Разнообразный и богатый опыт", 
  "Задача организации, в особенности же", "Идейные соображения высшего порядка, а также");
 let w2 = new Array ("реализация намеченных плановых заданий", "рамки и место обучения кадров", 
  "постоянный количественный рост и сфера нашей активности", "сложившаяся структура организации", 
   "новая модель организационной деятельности", "дальнейшее развитие различных форм деятельности", 
  "постоянное информационно-пропагандистское обеспечение нашей деятельности", 
  "укрепление и развитие структуры", "консультация с широким активом", 
  "начало повседневной работы по формированию позиции");
 let w3 = new Array ("играет важную роль в формировании", "требует определения и уточнения", 
  "способствует подготовке и реализации", "обеспечивает широкому кругу специалистов участие в формировании", 
  "позволяет выполнить важные задания по разработке", "в значительной степени обусловливает создание", 
  "позволяет оценить значение", "представляет собой интересный эксперимент проверки", 
  "влечет за собой процесс внедрения и модернизации");
 let w4 = new Array ("существенных финансовых и административных условий", "дальнейших направлений развития", 
  "системы массового участия", "позиций, занимаемых участниками в отношении поставленных задач", 
  "новых предложений", "направлений прогрессивного развития", 
  "системы обучения кадров, соответствующей насущным потребностям", 
  "соответствующих условий активизации", "модели развития", "форм воздействия");
 let phrase = "";
 for (let i = 0; i < count; i++) {
  let rnd1 = Math.floor(Math.random() * w1.length);
  let rnd2 = Math.floor(Math.random() * w2.length);
  let rnd3 = Math.floor(Math.random() * w3.length);
  let rnd4 = Math.floor(Math.random() * w4.length);
  phrase += "<p>" + w1[rnd1] + " " + w2[rnd2] + " " + w3[rnd3] + " " + w4[rnd4] + ".</p>\n";
 }
 document.getElementById(id).innerHTML = phrase;
}

generateText('textContent',4); //id раздела, количество предложений
</script>
13. Составить частотную таблицу слов в документе

Что входит в "слова" - видно из text.replace, таблица формируется просто по порядку встречаемости слов в тексте. Предполагается только латиница и/или кириллица, регистр символов должен игнорироваться.

<p>Текст</p>
<textarea id="input_text"></textarea>
<br>
<button id="btn">Анализировать</button>
<table width="200" style="border-collapse: collapse;">
 <caption>Таблица частот слов</caption>
 <thead>
  <tr>
   <th>Слово</th>
   <th>Частота</th>
  </tr>
 <thead>
 <tbody id="tbody"></tbody>
</table>
<script>
 let input = document.getElementById('input_text');
 let btn = document.getElementById('btn');
 let tbody = document.getElementById('tbody');
 let wordsArray = [];
 function countFrequency() {
  if (tbody.hasChildNodes()) {
   while (tbody.firstChild) {
    tbody.removeChild(tbody.firstChild);
   }
  }
  let text = input.value.toString();
  text = text.replace(/[^\w\s\A-Za-zА-Яа-яЁё0-9\-]/gi,' '); //что входит в "слова"
  words = text.split(/\s/).filter(function(x){return x.length>0; }); //убрали пустые
  for (let word of words) {
   word = word.toLowerCase();
   let row = document.getElementById('tr-'+word);
   if (row == null) {
    let tr = document.createElement('tr');
    tr.id = 'tr-'+word;
    let td1 = document.createElement('td');
    let td2 = document.createElement('td');
    td1.innerHTML = word;
    td2.innerHTML = 1;
    tr.appendChild(td1);
    tr.appendChild(td2);
    tbody.appendChild(tr);
   }
   else {
    let value = row.childNodes[1].innerHTML;
    value++;
    row.childNodes[1].innerHTML = value;
   }
  }
 }
 btn.addEventListener('click',countFrequency);
</script>
14. Получить в разделе документа календарь на произвольные месяц и год

Всё пишется в раздел с id="calendar"

<style>
 .mtab {
  margin: 0 auto; 
  width: 32rem;
  font-family: monospace;
  border-spacing: 1.5rem 0;
 }
 .mrow {
 }
 .mcol {
  text-align: left;
 }
 .mp {
  margin: 0.25rem 0;   
 } 
</style>

<div class="mtab">
 <form name="dataForm" id="dataForm" class="mtab">
  <select size="1" name="monthList" id="monthList"></select>
  <script>
   let d = new Date(), m = d.getMonth(), str = '';
   let names = new Array ('Январь','Февраль','Март','Апрель','Май','Июнь',
    'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь');
   for (let i=0; i<12; i++) 
    str += '<option value="'+i+'"'+(i==m?' selected':'')+'>'+names[i]+'</option>'+"\n";
   document.getElementById('monthList').innerHTML = str+"\n";
  </script>
  <select size="1" name="yearsList" id="yearsList"></select>
  <script>
   let y = d.getFullYear(), y2 = y + 100, y1 = 1582;
   str = '';
   for (let i=y2; i>=y1; i--) 
    str += '<option value="'+i+'"'+(i==y?' selected':'')+'>'+i+'</option>'+"\n";
   document.getElementById('yearsList').innerHTML = str+"\n";
  </script>
  <input type="button" value="Сформировать" 
   onclick="Calendar(document.dataForm.monthList.options[document.dataForm.monthList.selectedIndex].value,
                     document.dataForm.yearsList.options[document.dataForm.yearsList.selectedIndex].value,'calendar');">
  <input type="button" value="Текущий" onclick="currentCalendar('calendar');">
 </form>
</div>
<div id="calendar" class="mtab"></div>

<script>
 function Calendar(m,y,id) { //месяц (с 0), год (полный), id раздела, куда вставить календарь
  function isLeapYear (y) { return (((y%4==0) && (y%100!=0) || (y%400==0)) ? true : false); }
  function getDaysInMonth (m,y) {
   let mondays = [31,28,31,30,31,30,31,31,30,31,30,31];
   if (isLeapYear (y) == true) mondays[1]=29;
   return (mondays[m]);
  }
  let s = '<table class="mtab">'+"\n", d = new Date(y,m,1), w = d.getDay(), maxd = getDaysInMonth(m,y);
  if (w == 0) w = 6; else w--;
  let arr = Array(maxd).fill(0).map(function(val,index){return index+1;}); //1..maxd
  for (let i=0; i<w; i++) arr.unshift(' '); //пустые дни впереди
  for (let i=arr.length; i<42; i++) arr.push(' '); //пустые дни в конце
  s += '<tr class="mrow">'+"\n";
  let wd = new Array ('Пн','Вт','Ср','Чт','Пт','Сб','Вс');
  for (let i=0; i<7; i++) s += '<th>'+wd[i]+'</th>'+"\n";
  s += '</tr>'+"\n";

  let k = 0;
  for (let r=0; r<6; r++) {  
   s += '<tr class="mrow">'+"\n";
   for (let c=0; c<7; c++) {  
    s += '<td class="mcol"><p class="mp">'+arr[k++]+'</p></td>'+"\n";
   }
   s += '</tr>'+"\n";
  }
  s += '</table>'+"\n";
  document.getElementById(id).innerHTML = s;
 }

 function currentCalendar (id) { 
  let d = new Date(), m = d.getMonth(), y = d.getFullYear();
  document.dataForm.monthList.selectedIndex = m;
  document.dataForm.yearsList.selectedIndex = 100;
  Calendar(m,y,id);
 }
</script>
<noscript>
 <div class="mtab">Включите JavaScript для работы приложения</div>
</noscript>
15. Получить список тегов документа

Берутся теги внутри <body>

 <script>
  function getTags() {
   function walk(node) {
    var child, next;
    if (node.nodeType) {
     if (node.tagName !== undefined) tags.push(node.tagName);
     child = node.firstChild;
     while ( child ) {
      next = child.nextSibling;
      walk(child);
      child = next;
     }
    }
   }
   let elem = document.body;
   let tags = new Array();
   walk (elem);
   return tags.join("\n"); //список названий тегов, разделенных переводом строки
  }
 </script>
 <p>Просто, чтоб были теги</p>
 <p></p>
 <img></img>
 <img></img>
 <img></img>
 <h5></h5>
 <p></p>
 <h5></h5>
 <button onclick="document.getElementById('result').innerHTML = getTags();">OK</button>
 <pre id="result"></pre>
16. Дата плюс/минус количество дней

В зависимости от знака второго аргумента, получаем новую дату, отстоящую от даты первого аргумента назад или вперёд на нужно количество дней.

<script>
 function getDateAgo (date, days) {
  let dateCopy = new Date(date);
  dateCopy.setDate(date.getDate() + days);
  return dateCopy;
 }
 alert( getDateAgo(new  Date(), -30));
</script>
17. Самосчётная функция

Без привлечения внешней переменной считаем , сколько раз была вызвана функция

<script>
 function cnt() {
  if ( typeof(cnt.c) == 'undefined' ) {
   cnt.c = 0;
  }
  cnt.c++;
  return cnt.c;
 }

 cnt();
 cnt();
 let n=cnt();
 alert(n);
</script>
18. Упрощение дроби

Вводим из формы целые значения для числителя и знаменателя, сокращаем дробь, учитывая знак и особые случаи.

<form name="dataform">
<p>Введите числитель: <input type="number" name="num" value="0"></input></p>
<p>Введите знаменатель: <input type="number" name="denom" value="0"></input></p>
<input type="button" value="Упростить" 
 onclick="document.getElementById('result').innerHTML = 
          simplifyFraction(document.dataform.num.value,document.dataform.denom.value);">
</form>
<div id="result"></div>

<script>
 function simplifyFraction (num,denom) {
  let M = parseInt(num), N = parseInt(denom);
  let sgn = 0; if (M<0) sgn++; if (N<0) sgn++; 
  sgn %= 2; //- на - дает снова +
  let m = Math.abs(M), n = Math.abs(N); M = m, N = n;
  if (!n) return (sgn?'-':'')+'&infin;';
  for (let i = 2; i <= m; i++) {
   if (m % i == 0 && n % i == 0) {
    M = m / i; N = n / i;
   }
  }
  return (sgn?'-':'') + M + (N!=1 ? '/'+N : ''); 
 }
</script>
19. Отрисовка на канве геометрического примитива

Здесь мы изобразим суперэллипс.

<canvas id="canvas" style="border: 1px dotted red;"></canvas>
<script> 
function start (id,n,a,b) {
 function point (x, y) { ctx.fillRect( x, y, 1, 1); }
  let canvas = document.getElementById(id);
  const PADDING = 20; //отступ
  let size = 2*Math.max(a,b)+PADDING; //размер канвы зависит от a,b
  canvas.width = canvas.height = size;
  let ctx = canvas.getContext( "2d" );
  ctx.rect (0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "#fff"; ctx.fill(); //белая заливка
  ctx.fillStyle = "#000"; //отрисовка чёрным
  for (let t=0; t <= 2*360; t += 0.1) { //прорисовываем дважды для надёжности
   x = Math.pow( Math.abs( Math.cos( t ) ), 2 / n ) * a * Math.sign( Math.cos( t ) );
   y = Math.pow( Math.abs( Math.sin( t ) ), 2 / n ) * b * Math.sign( Math.sin( t ) );
   point (x + ( canvas.width >> 1 ), y + ( canvas.height >> 1 ));
  }
 }

 start ('canvas',2.5,200,200);
</script>
нарисованный суперэллипс, скриншот
нарисованный суперэллипс, скриншот
20. Как извлечь из текстового содержимого только числа в любой допустимой записи?

Метод getArrayFromTextarea извлекает и возвращает массив чисел из значения текстового контейнера с id==srcId. Обратите внимание также на "гибкую" функцию очистки полей clearMe.

 <script> 
  function getArrayFromTextarea (srcId) {
   let text = document.getElementById(srcId).value;
   let arr = text.split(/\s+/);
   arr = arr.filter( function(item){ return item!='' && !isNaN(item) && isFinite(item); });
   return arr;
  }

  function clearMe (...ids) { //очистить элементы из ids
   if (ids === undefined) return;
   for (let id in ids) {
    let elem = document.getElementById(ids[id]);
    if (!elem) continue;
    elem.value = '';
   }
  }

  function selectMe (id) { //выделить элемент с id
   document.getElementById(id).focus();
   document.getElementById(id).select();
  }
 </script> 
 <noscript>  
  <p>Включите Javascript в браузере для работы приложения</p>
 </noscript> 
 
 <p>Введите или вставьте числа:</p>
 <p><textarea id="src" rows="5" cols="90" style="font=family: monospace;"></textarea></p>
 <p>Результат обработки:</p>
 <p><textarea id="res" rows="5" cols="90" style="font=family: monospace;"></textarea></p>
 <p>
  <button onclick="document.getElementById('res').value = 
   getArrayFromTextarea('src').join('\n'); selectMe('res');">Выполнить</button>
  <button onclick="clearMe('src','res');">Очистить</button>
 </p>
21. Можно ли делать арифметические действия с длинными целыми числами нативными методами, а не "вручную", как в задаче 11?

В Javascript уже есть BigInt, правда, работает не везде.

В этом скрипте также особо вычурная функция clearMe очистки нужных свойств у нужных элементов.

<script> 
 function actionMe (id1, id2, action) {
  let n1 = BigInt(document.getElementById(id1).value),
   n2 = BigInt(document.getElementById(id2).value),
   res;
  switch (action) {
   case '*': res = n1 * n2; break;
   //добавьте другие действия
   default: res = 'Неверная операция'; break;
  }
  return res;
 }
 function clearMe (...ids) { 
  //очистить свойства элемент property у элементов с id="item" из ids
  if (ids === undefined) return;
  for (let id in ids) {
   let idn = ids[id].item;
   let elem = document.getElementById(idn);
   if (!elem) continue;
   let prop = ids[id].property;
   if (!prop) continue;
   elem[prop] = '';
  }
 }
</script> 
<noscript>  
 <p>Включите Javascript в браузере для работы приложения</p>
</noscript> 
 
 <p>Число 1: <input type="text" id="n1" size="80" maxlength="128" pattern="-?\d+"></p>
 <p>Число 2: <input type="text" id="n2" size="80" maxlength="128" pattern="-?\d+"></p>
 <p>
  <button onclick="document.getElementById('result').innerHTML = actionMe('n1','n2','*');">[*]</button>
  <button onclick="clearMe(
   {item: 'n1', property: 'value'}, 
   {item: 'n2', property: 'value'}, 
   {item: 'result', property: 'innerHTML'}, 
  );">clear</button>
 </p>
 <p id="result"></p>
22. Как измерить время выполнения вычислительного процесса?
<script>
 let start, end;
 start = new Date();
 for (let i = 0; i < 1e7; i++) { //10 миллионов шагов
  Math.sqrt(i);
 }
 end = new Date();
 alert ((end.getTime() - start.getTime()) + ' мсек');
</script>
23. Как создать на странице динамический элемент, настроить его атрибуты и стилевые селекторы, добавить обработчик события и выполнение кода по таймеру?

Все ответы - в этом небольшом коде с комментариями.

<div id="content"></div> <!-- Элемент для контента -->
<script>
 function onload (id) { //Функция-обёртка приложения
  let elem = document.getElementById(id); //Получить основной элемент для контента
  if (!elem) { console.log('Item id='+id+' not found'); return; }
  let child = document.createElement('p'); //Создать новый абзац
  child.setAttribute('align', 'center'); //Так можно настроить атрибуты тега
  child.style.backgroundColor = '#999'; //И стилевые селекторы
  child.innerHTML = 'Click me'; //И содержимое HTML
  child.addEventListener ('click', function(e) { click1(e); });
   //Добавить обработчик события для элемента
  elem.appendChild (child); //Добавить сам элемент в основной

  function reDraw () { child.innerHTML = new Date(); } //Перерисовка

  function click1 (e) { //Выполнить reDraw и назначить её вызов с таймером 1 сек.
   reDraw();
   let timer = setInterval(function () { reDraw();  }, 1000); 
  }
 }

 window.addEventListener('load', function (e) { onload('content'); }, false); 
  //Выполнить функцию-обёртку по загрузке страницы, без захвата событий
</script>
24. Как избежать обращения из функций к "внешним" по отношению к коду элементам HTML?

Передавать в функции аргументами нужные данные из элементов HTML, если эти данные требуются функциям.

Функции пусть выполняют свою работу - получают данные через аргументы и возвращают результат работы в виде переменной, массива или объекта.

А если писать каждый раз document.getElementById(id).value или document.getElementById(id).innerHTML слишком долго, простейшем случае можно сделать объект-"обёртку" с именами методов покороче, например, как $ в этом коде:

<script>
 function truncate (src, maxlen) {
  return src.length < maxlen ? src : src.substring(0,maxlen)+'...';
 }

 let $ = {
  get (id) { return document.getElementById(id) ? document.getElementById(id) : null; },
  getVal (id) { return document.getElementById(id) ? document.getElementById(id).value : null; },
  getHTML (id) { return document.getElementById(id) ? document.getElementById(id).innerHTML : null; },
  setVal (id, val) { if (document.getElementById(id)) document.getElementById(id).value = val; },
  setHTML (id, html) { if (document.getElementById(id)) document.getElementById(id).innerHTML = html; }
 };
</script>
	
<textarea name="str" id="str"></textarea>
<br><input type="text"  name="maxlength" id="maxlength" value="10">
<br><textarea name="myResult" id="myResult" readonly></textarea>
<br><input type="button" value="Выполнить" 
 onclick="$.setVal('myResult',truncate($.getVal('str'),$.getVal('maxlength')));">
25. Как программно выделить нужную часть текста в текстовом поле ввода?

Можно, например, так (идентификатор элемента и номера позиций в тексте передаются аргументами функции).

<input type="text" id="txt">
<input type="button" value="Выделить" onclick="createSelection('txt', 1, 3);">
<script>
function createSelection(id, start, end) {
 let field = document.getElementById (id);
 if (field.createTextRange) {
  let selRange = field.createTextRange();
  selRange.collapse(true);
  selRange.moveStart('character', start);
  selRange.moveEnd('character', end);
  selRange.select();
  field.focus();
 } 
 else if (field.setSelectionRange) {
  field.focus();
  field.setSelectionRange(start, end);
 } 
 else if (typeof field.selectionStart != 'undefined') {
  field.selectionStart = start;
  field.selectionEnd = end;
  field.focus();
 }
}
</script>
26. Как реализовать операции над множествами?

В современных версиях Javascript есть отдельный объект Set.

<script>
 function isSuperset (set, subset) {
  for (let elem of subset) {
   if (!set.has(elem)) return false;
  }
  return true;
 }

 function union (setA, setB) {
  let _union = new Set(setA);
  for (let elem of setB) _union.add(elem);
  return _union;
 }

 function intersection (setA, setB) {
  let _intersection = new Set();
  for (let elem of setB) {
   if (setA.has(elem)) _intersection.add(elem);
  }
  return _intersection;
 }

 function difference(setA, setB) {
  let _difference = new Set(setA);
  for (let elem of setB) _difference.delete(elem);
  return _difference;
 }

let setA = new Set([1, 2, 3, 4]),
 setB = new Set([2, 3]),
 setC = new Set([3, 4, 5, 6]);

console.log ( isSuperset(setA, setB) );   // => true
console.log ( union(setA, setC) );        // => Set [1, 2, 3, 4, 5, 6]
console.log ( intersection(setA, setC) ); // => Set [3, 4]
console.log ( difference(setA, setC) );   // => Set [1, 2]
</script>
27. Как выделить все вхождения регулярного выражения в строку?

В нашем случае выделение сводится к подчёркиванию найденных вхождений, но принцип обработки не изменится и для других подобных задач.

<p id="result"></p>
<script>
 let str = 'Привет, я очередная строка текста "№;%::?6абв с цифрами 123 и буквами 456! Привет!!';
  //строка откуда-то
 let reg = /[а-яё]+/gi; 
  //регулярка, модификатор g - глобально, i - без регистра букв
 let res = selectByRegex (str,reg); //найти и выделить все вхождения reg в str
 document.getElementById('result').innerHTML = res ? res : 'не найдено';

 function selectByRegex (str,reg) { 
  //Аргументы - строка, регулярка. Вернёт строку, где всё найденное подчёркнуто (или null)
  let str2 = ''; 
  let matches = str.match (reg);
  if (!matches) return null;
  //console.log(matches);
  for (let m in matches) {
   let before = str.indexOf(matches[m]);
   if (before > -1) { //часть до вхождения
   let bstr = str.substring(0,before);
    str2 += bstr; //добавить в целевую строку
    str = str.substring (bstr.length); //и убрать из исходной
   }
   str2 += '<u>' + matches[m] + '</u>'; //подчеркнуть вхождения
   str = str.substring (matches[m].length); //и убрать обработанное
  }
  str2 += str; //хвост строки, если есть
  return str2;
 }
</script>
28. Почему я не могу ввести текст регулярного выражения в поле ввода или строку и потом использовать его в RegExp?

Потому что в поле формы (или в переменную) Вы вводите строку, а регулярное выражение - это объект. Помочь может показанный ниже код, который конструирует регулярку из поля ввода с id="regexp" и применяет её к строке из поля ввода с id="source". Сама функция обработки findAllRegexFromInput просто составляет массив из вхождений регулярного выражения в строку и возвращает строку со списком вхождений, разделённых переводами строк.

<p>
 Текст: <input type="text" size="80" id="source" 
  value="Привет, я очередная строка текста &quot;№;%::?6абв с цифрами 123 и буквами 456! Привет!!">
 RegExp: / <input type="text" size="15" id="regexp" value="[а-яё]+"> /
 <input type="checkbox" id="gFlag" checked><label for="gFlag">G</label>
 <input type="checkbox" id="iFlag" checked><label for="iFlag">I</label>
 <input type="button" value="Выполнить" 
  onclick="
   let str = document.getElementById('source').value,
    regStr = document.getElementById('regexp').value; 
    //В регулярке только основная часть, без обрамления / ... /
   let flags = '';
   if (document.getElementById('gFlag').checked) flags += 'g';
   if (document.getElementById('iFlag').checked) flags += 'i';
   let reg = new RegExp(regStr, flags); //Флаги приходится обрабатывать отдельно
   let res = findAllRegexFromInput (str, reg);
   document.getElementById('result').innerHTML = res ? res : 'не найдено';"
 >
</p>
<p id="result"></p>
<script>
 function findAllRegexFromInput (str, reg) { 
  let arr = []; 
  let matches = str.match (reg);
  if (!matches) return null;
  for (let m in matches) arr.push (matches[m]);
  return arr.join("<br>\n");
 }
</script>
29. Для внешнего файла .js вместо кириллицы в браузере показываются "кракозябры", хотя везде кодировка UTF-8. Как исправить?

Под "кракозябрами" имеется в виду, прежде всего, испорченный Юникод.

Если вдруг ваш сервер не отдаёт в заголовке HTTP-запроса кодировку для js-файлов, укажите дополнительный атрибут charset в теге подключения скрипта, то есть, не

<script src="js/script.js"></script>

а

<script src="js/script.js" charset="utf-8"></script>

30. Как преобразовать строку в последовательность Юникод-символов и наоборот?

То есть, "foo АЯая № bar" становится "foo \u0410\u042f\u0430\u044f \u2116 bar", а потом всё обратно.

<div id="result"></div>
<script>
 function chars2utf (s) {
  return s.replace (/[\u0080-\uFFFF]/g,
    function (s) { return "\\u" + ('000' + s.charCodeAt(0).toString(16)).substr(-4); }
  );
 }

 function utf2chars (s) {
  let decoded = JSON.parse('{"data": "'+ s +'"}'); //Декодируем обратно
   //метод escape устарел!
  return decoded.data;
 }

 let s = chars2utf('foo АЯая № bar');
 document.getElementById('result').innerHTML = s;
  // "foo \u0410\u042f\u0430\u044f \u2116 bar"
 s = utf2chars (s);
 alert (s); //foo АЯая № bar
</script>
<noscript>Включите Javascript в браузере!</noscript>

При декодировании в строке не должно быть двойных кавычек или они должны быть перед прямым преобразованием экранированы по принципу " -> \\"

31. Как получить callback-функцию из строки с записью нужного оператора?

"Опасная" функция eval не нужна. Просто укажите там, где нужна callback-функция, конструкцию вида

new Function(callback)()
где callback - строка с оператором Javascript. Пример (использует локальную копию мини-фреймворка Fader):

 <script src="http://blog.kislenko.net/archives/13965.js" charset="utf-8"></script>
 <script>
  let buttonsSwitch3 = true;
  function fadeDemo3(elem,value,callback) {
   if (buttonsSwitch3){
    Fader.fadeOutElement(elem, Math.abs(parseFloat(value)),
     new Function(callback)()
    );  
   }  
   else {
    Fader.fadeInElement(elem, Math.abs(parseFloat(value)),
     new Function(callback)()
    );
   }
   buttonsSwitch3 = !buttonsSwitch3;
  }
 </script>
 <p>Задержка анимации:
  <input type="number" id="fadeNumber3" min="0" max="1" value="0.1" step="0.1" size="3" maxlength="3">
  <input type="button" value="Выполнить" id="fadeButton3" 
   onclick="fadeDemo3(document.getElementById('fadeId3'),document.getElementById('fadeNumber3').value,
    document.getElementById('funcSelector').value);">
 </p> 
 <p>Выберите функцию для выполнения после эффекта:
  <select id="funcSelector">
   <option value="" selected>Ничего не делать</option>
   <option value="alert('ok')">Вывести окно сообщения</option>
   <option value="location.assign('http://disney.ru')">Перейти на другой сайт</option>
   <option value="document.body.style.backgroundColor = 'yellow'">Изменить фон страницы</option>
   <option value="location.reload()">Перезагрузить страницу</option>
  </select>
 </p>
 <p id="fadeId3"><i>Элемент для проверки эффектов "fadeOutElement"/"fadeInElement"</i></p>
 <p>На самом деле, из-за асинхронного выполнения Javascript функция необязательно 
  будет срабатывать после завершения эффекта.</p>

 Файл с фреймворком Fader из примера (прямая ссылка на файл .js) (3 Кб)

32. Как динамически создать "стек" элементов с обработчиками событий?

См. также пример 23. На самом деле, в том и удобство, что нет никакой разницы между элементами, прописанными в разметке и созданными динамически.

Например, здесь мы создаём "стек" из элементов <textarea> и назначаем динамически создаваемым элементам обработчики событий (клика по <textarea>).

<input type="button" onclick="addItem();" value="Добавить">
<input type="button" onclick="deleteItem();" value="Удалить">
</p>
<script>
 let i = 1;
 function addItem() {
  let elem = document.createElement('textarea');
  if (!elem) { alert ('Adding error'); return; }
  elem.setAttribute ('id','area_' + i);
  elem.setAttribute ('onclick','clickItem("area_' + i + '");');
  elem.value = i;
  document.body.appendChild (elem);
  i++;
 }
 function deleteItem() {
  let elem = document.getElementById('area_'+(i-1));
  if (!elem) { alert ('Deleting error'); return; }
  document.body.removeChild (elem);
  if (i > 1) i--;
 }
 function clickItem (id) {
  let elem = document.getElementById(id);
  if (!elem) return;
  alert ('Ok, my id is '+id);
 }
</script>
33. Как принудительно вывести вещественные числа с разделителем дробной части - запятой?

В примере пишем в элемент 10000 чисел, довольно быстро, так как split + join заведомо быстрее, чем RegExp.

<pre>
 <div id="resRandom"></div>
</pre>
<script>
let n = 10000, s = '';
for (let i=0; i<n; i++) {
 s += Math.random().toString().split('.').join(',') + "\n";
}
document.getElementById('resRandom').innerHTML = s;
</script>

теги: список учебное алгоритм javascript избранное

26.04.2020, 15:25; рейтинг: 652