Пользователь
0,0
рейтинг
15 ноября 2013 в 18:27

Разработка → Крошечный Excel на чистом JavaScript (30 строк кода) перевод

Особенности:
  • Около 30 строк обычного JavaScript
  • Использованные библиотеки: отсутствуют
  • Синтаксис как в Excel (формулы начинаются с "=")
  • Поддерживаются произвольные выражения(=A1+B2*C3)
  • Обнаруживаются циклические ссылки
  • Автоматическое сохранение в localStorage

image


HTML:
<table></table>


CSS:
input {
    border: none;
    width: 80px;
    font-size: 14px;
    padding: 2px;
}

input:hover {
    background-color: #eee;
}

input:focus {
    background-color: #ccf;
}

input:not(:focus) {
    text-align: right;
}

table {
    border-collapse: collapse;  
}

td {
    border: 1px solid #999;
    padding: 0;
}

tr:first-child td, td:first-child {
    background-color: #ccc;
    padding: 1px 3px;
    font-weight: bold;
    text-align: center;
}



JavaScript код:
for (var i=0; i<6; i++) {
    var row = document.querySelector("table").insertRow(-1);
    for (var j=0; j<6; j++) {
        var letter = String.fromCharCode("A".charCodeAt(0)+j-1);
        row.insertCell(-1).innerHTML = i&&j ? "<input id='"+ letter+i +"'/>" : i||letter;
    }
}

var DATA={}, INPUTS=[].slice.call(document.querySelectorAll("input"));
INPUTS.forEach(function(elm) {
    elm.onfocus = function(e) {
        e.target.value = localStorage[e.target.id] || "";
    };
    elm.onblur = function(e) {
        localStorage[e.target.id] = e.target.value;
        computeAll();
    };
    var getter = function() {
        var value = localStorage[elm.id] || "";
        if (value.charAt(0) == "=") {
            with (DATA) return eval(value.substring(1));
        } else { return isNaN(parseFloat(value)) ? value : parseFloat(value); }
    };
    Object.defineProperty(DATA, elm.id, {get:getter});
    Object.defineProperty(DATA, elm.id.toLowerCase(), {get:getter});
});
(window.computeAll = function() {
    INPUTS.forEach(function(elm) { try { elm.value = DATA[elm.id]; } catch(e) {} });
})();


Код на JSFiddle — http://jsfiddle.net/zag2art/b3pbw/4/
Перевод: Ondřej Žára
Артур Заяц @zag2art
карма
187,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (98)

  • +18
    Блестяще!

    Оффтопик
    Автор оригинала так же создал отличную библиотеку для написания «рогаликов»:rot.js.
    • +2
      Да. Шайтан!
    • +20
      ох тыж — автор оригинала оказывается сидит через коридор от меня )
      надо будет зайти ) все таки мир маленький
      • +15
        Передавайте респекты:)
    • +4
      Прошу прощение за безграмотность, но что такое «рогалик»?
      • 0
      • 0
        *Удалено, уже ответили*
  • 0
    У меня тоже была такая идея, но… не 30 строк кода. Это очень круто, спасибо.
  • +3
    Так как вычисление значений в ячейках происходит при помощи eval, можно использовать любые глобальные переменные и функции языка.
    • +13
      Более того, можно в этом «экселе» испольнять JS код и например писать 3D игры… это вам не картины в MS office рисовать.
      Ну а по теме кода — очень красиво и изящно, вроде все знакомые функции/методы, но я бы в 30 строк не уложился.
    • +4
      Вводим в ячейку, например, =alert('z') — и с вкладки невозможно выйти, каждый onBlur будет сопровождаться алертом.
      • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    JS-ninja.
  • 0
    Воистину, нет пределов совершенству.
  • 0
    Офигеть просто! Гуру.
  • +1
    Автор топика-ссылки, поправьте автора оригинала — Ondřej.
  • +10
    Код офигенный, но на самом деле тут больше строк, если следовать нормальным стайлгайдам.
    Поэтому вовсе не 30.
    • +2
      Вы за что человека минусуете? Или действительно считаете, что код должен выглядеть именно так:

      } else { return isNaN(parseFloat(value)) ? value : parseFloat(value); }
      

      INPUTS.forEach(function(elm) { try { elm.value = DATA[elm.id]; } catch(e) {} });
      
      • +12
        За то, что он зануда. Ну прогнал через beautifier, ну стало 40 строк. Велика разница.

        Было бы ещё понятно, если бы на самом деле™ там было тридцать однострочных функций в тысячу знаков каждая.
        • +3
          Занудство
          Человек объективно опроверг факт, который стоит на первом месте в списке достоинств скрипта, и правильно сделал
        • +2
          В том то и дело: зачем эта маленькая ложь? Если бы он написал «в 40 строк», никто бы меньше восхищаться не стал, но и не было бы этого неприятного разговора.
        • +6
          Напомнило:
          <insomnia> Нужно выполнить всего три команды, чтобы поставить Gentoo
          <insomnia> cfdisk /dev/hda && mkfs.xfs /dev/hda1 && mount /dev/hda1 /mnt/gentoo/ && chroot /mnt/gentoo/ && env-update &&. /etc/profile && emerge sync && cd /usr/portage && scripts/bootsrap.sh && emerge system && emerge vim && vi /etc/fstab && emerge gentoo-dev-sources && cd /usr/src/linux && make menuconfig && make install modules_install && emerge gnome mozilla-firefox openoffice && emerge grub && cp /boot/grub/grub.conf.sample /boot/grub/grub.conf && vi /boot/grub/grub.conf && grub && init 6
          <insomnia> это первая
      • +1
        <irony>Конечно же так коду выглядеть нельзя! Зачем вокруг return фигурных скобок наставили?!</irony>
    • +1
      А если еще учесть код HTML + CSS… )
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      [sarcasm] видимо, весь отдел теперь ищет новую работу [/sarcasm]
    • +4
      Сенсация! Утекли исходники Office 365!

      Или это все таки Google Spreadsheet?
      • +4
        В гуглодоксе можно работать над одним файлом сразу нескольким человекам так что исходники гуглодокса тут: jsfiddle.net/sy85U/
  • 0
    Очень круто. А может кто объяснить почему [].slice.call(document.querySelectorAll("input")); а не document.querySelectorAll("input");?
    • +3
      `querySelectorAll` возвращает `Array-like` объект, а с помощью `slice` мы получаем уже настоящий `Array`, поэтому можем использовать `forEach`

      По аналогии иногда используют `Array.prototype.slice.call(arguments)`, так как `arguments` также не `Array`.
      • 0
        Спасибо, понятно. А это вообще безопасно вызывать метод Array-а на объекты которые на самом деле не Array?
        • +3
          Методы Array делались таким образом, чтобы максимально беспроблемно работать на array-like объектах. Так что, возможно, есть исключения, которых я щас не вспомню, но в целом это не только безопасно, но и официально одобрено Минздравом.
          • 0
            Дважды спасибо!
          • 0
            Исключения есть: IE8. Метод apply не воспринимает хеши с целочисленными свойствами совместно со свойством length.
        • +2
          Именно, что расценивать как небезопасно? Можно любой объект таким образом превращать в `Array`. Берется `length` property (если есть) и заполняется массив «undefined» или index values.
          Array.prototype.slice.call({}) // -> [ ]
          Array.prototype.slice.call({1: 0, length: 2}) // -> [ null, 0]
          Array.prototype.slice.call({0: 1, 1: 2, length: 2}) // -> [ 1, 2]
          
          • 0
            Если так можно делать, то безопасно. Я это уже понял :)
    • 0
      Можно еще так:

      Array.apply(null, document.querySelectorAll('div'));
      
      • 0
        если уж так пошло, то лучше бинд на INPUTS
        • +1
          Чего?
          • +1
            iterateCells = Array.prototype.forEach.bind(document.querySelectorAll("input"));
            и заменить INPUTS.forEach на iterateCells. jsbin.com/EKoviqEn/2/edit
            вместо Array.prototype можно написать [], но это все равно.
            • 0
              Чем лучше-то?
              Лучше вообще не использовать forEach — jsbin.com/OxuqUNoX/7/edit
  • –2
    Может стоит добавить «Ненормальное программирование»?
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Доступны те операции, которые поддерживает сам JavaScript, судя по тому, что вычисление делается eval'ом.
    • –4
      Даже «=alert("OK")». При всей крутизне, это решение не для продакшена.
      • +14
        Елки! Срочно откатываемся!!!
  • +20
    Это, конечно, прикольно, но сколько сотен тысяч строк кода будет в скрипте, если добавить возможность форматирования, объединение ячеек, сортировку, группировку, выделение, фильтры, графики, предустановленные формулы, макросы, внешние данные и многое-многое другое?
    Автору респект за смекалку, но называть это Excel — то же самое, что <textarea /> назвать Word'ом
    • +2
      Все верно, но говорят что 90% пользователей пользуются 10% возможностями программы.
      Когда был студентом, проходил летнюю «практику» в универе. Помогал электрикам. Мне ка то начальство принесло огромную таблицу на ватмане а0, какая то ведомость, которую нужно было подсчитать. Компьютера тогда не было ни у меня ни в бухгалтерии. Считать на калькуляторе и сто раз перепроверять было лень. Скачал себе на телефон мидлет, который весил килобайт 20-30. И вот на телефоне с экраном 128х128 за час вбил все данные и получил результат.
      • +11
        Вот этим мы и отличаемся. Вместо того, чтобы делать руками, мы пол дня пишем программу, вторую половину дня отлаживаем её, зато потом нажимает одну кнопку и за пару минут все само считается. Надо мной жена постоянно издевается по этому поводу))
        • +2
          Аха, как то мне надоело нажимать хоткей из трех кнопок, в результате недели три времени ушли на создание одного проекта )
        • +2
          И зря. Этот код мы выкладываем на гитхаб и другие люди тратят намного меньше времени, зато пишут код для других задач. Это называется прогресс. А ещё это просто интереснее рутины.
        • +3
          Записки жены программиста
          [...] поэтому Серега сел делать приглашения.

          Делал он их недолго. Дня два. Лично я думала, что он просто заготовит текст в Word-е, после чего впишет туда имена из списочка, который я ему дала, а затем распечатает, но Серега сказал, что настоящие программеры так не поступают. Настоящие программеры, заявил Серега, существа крайне ленивые, поэтому быстренько создают программу, чтобы она сделала все за них, а список загоняют в комп через сканер. С этими словами он засел, как он выразился, «ваять программулю», которая сама будет брать данные из списка, а затем распечатывать готовые приглашения.

          Первоначально мне эта идея даже понравилось. Действительно, чего пыхтеть и забивать данные руками, когда можно загнать данные через сканер, а крутая программуля, написанная крутым программером, сама все распечатает. Однако через пару дней стало понятно, что избранный Серегой способ не так эффективен, как казалось на первый взгляд. Во-первых, программа распознавания текста отказалась четко идентифицировать буквы, написанные мною от руки. Серега заявил, что это не беда, и что он быстренько обучит программу работе с моим почерком. Убив на этот процесс каких-то четыре часа, Серега заявил, что я все время по-разному пишу одинаковые буквы, поэтому программа ничего распознать не может. Пришлось мне тащиться домой, доставать папулькину старую пишущую машинку и выстукивать текст на ней, чтобы программа его могла распознать.

          Машинка помогла: текст распознался примерно с 75-процентной эффективностью, поэтому на исправление ошибок распознавания ушло не больше часа (приходилось все проверять с особым старанием, потому что люди обычно не прощают описок в своем имени и фамилии). Когда имена приглашенных наконец были занесены в компьютер, Серега сел «добивать программулю». В первый день окончания «добивания» я так и не дождалась, поэтому ушла домой. Серега остался сидеть за компьютером, изрыгая проклятия, потому что он решил писать программу сразу на новой версии языка, а она его не очень-то слушалась. Когда я появилась на следующий день, Серега сказал, что программа почти готова, и что осталось только выловить некоторые баги. Я ответила, что у нас нет времени ходить на соседний стадион, и я вообще не очень понимаю, как мы поймаем багги, несущийся на полном ходу. Но оказалось, что речь идет не о скоростном автомобильчике, а о всяких сбоях в работе программы, выловить которые, по словам Сергея, намного труднее, чем написать саму программу. На мой вопрос, зачем он вообще затеял всю эту бодягу, когда намного быстрее было бы просто в Word-е вписать имена, Серега ответил, что подобный ламерский подход к лицу какой-нибудь секретарше, но совсем не ему, крутому программеру, и что он после свадьбы еще займется моим воспитанием.

          Все баги Серега выловил часа за четыре, потянулся и сказал, что приглашения можно уже вставлять в принтер. При этом выяснилось, что открытки приглашений никто из нас не купил, так как Серега считал, что их куплю я, а я… Ну, сами понимаете. Пришлось мне бежать в ближайший газетный киоск и покупать приглашения. Купила, принесла. Заправили их в принтер. Серега сделал горделивое выражение лица и запустил программу. Принтер засосал первое приглашение. Серега весь раздулся от гордости. Принтер немного подумал, после чего выплюнул совершенно пустую открытку. Серега недоуменно поднял одну бровь. Принтер засосал следующее приглашение. Подумал. Снова выкинул пустое приглашение. Серега приподнял другую бровь. На третьем пустом приглашении брови у Сереги закончились, он остановил программу и стал разбираться, в чем дело.

          Оказалось, как радостно заявил Серега, он забыл обнулить каунтер. Обнулив каунтер, Серега снова запустил программу. На этот раз принтер засосал приглашение и стал на нем что-то увлеченно печатать. После этого выплюнул приглашение, засосал следующее и снова начал печатать. Серега снова надулся от гордости и стал мне доказывать преимущества программерского подхода перед ламерским. Однако я обратила внимание на то, что принтер уж как-то очень быстро выплевывает открытки, подошла посмотреть и обнаружила, что на каждой открытке значится только ФИО, но никакого текста приглашения нет.

          Тут Серега позеленел, снова полез в программу и стал там с таким ожесточением ковыряться, что я уже боялась за целостность его компьютера. Через полчаса Серега исправил и этот глюк, запустил программу и… наконец-то, первое приглашение было напечатано совершенно правильно! Правда, выглядело оно следующим образом:

          „®а®Ј®© Сергей Иванович!

          €а Ё ‘ҐаЈҐ© Ё¬Ґов зҐбвм ЇаЁЈ« бЁвм ў б ­ бў ¤мЎг,
          Є®в®а п б®бв®Ёвбп 19 ­®пЎап ў Ї®¬ҐйҐ­ЁЁ Є дҐ "‡ўҐ§¤®зЄ "
          Ї® ¤аҐбг: Ѓ®«ми®© ‚®а®иЁ«®ўбЄЁ© вгЇЁЄ, ¤®¬ 8, Є®аЇгб 2,
          бв஥­ЁҐ 3, 5 нв ¦ ­ Їа ў® ®в «Ёдв. Ћеа ­­ЁЄг бЄ ¦ҐвҐ, зв®
          ®в ‘ҐаЈҐп ‚ ᨫ쥢Ёз.

          Ќ бзҐв Ї®¤ аЄ®ў ¬®¦­® ­Ґ ЎҐбЇ®Є®Ёвмбп, ¤®бв в®з­® Ўг¤Ґв
          Їа®бв® ЇаЁ­ҐбвЁ б б®Ў®© ­ҐЎ®«ми®© Є®­ўҐав.

          Ѓг¤Ґ¬ ®зҐ­м а ¤л ў б ўЁ¤Ґвм.

          €а Ё ‘ҐаЈҐ©.

          Серега, посмотрев на это безобразие, сказал, что беспокоиться нечего, потому что тут просто что-то напутано с кодировками. Он снова полез в программу, и через каких-то 15 минут случилось ЧУДО — первое приглашение было отпечатано так, как надо. На втором, правда, у Сереги в принтере закончился картридж с чернилами.


          Уж не знаю, вдруг на свете есть ещё люди, которые это не читали:
          www.exler.ru/novels/wife.htm
      • +3
        Когда я был в армии (в очень отдалённой точке), к нам приезжал бухгалтер выдавать зарплату офицерам, это было начало 2000-х. Отмечу, что у военнослужащих очень сложная система начисления з/п, она сильно зависит от звания, должности, срока и географии службы.
        У бухгалтера с собой был только сейф с деньгами и дискета с XLS документом.
        Требовалось вбить несколько параметров в специально отведённые ячейки и на выходе получалась сумма, которую необходимо выплатить офицеру. Документ содержал огромное количество формул и констант, не захламляя рабочий лист с фамилиями и необходимыми параметрами. При том, что весь функционал, находящийся «под капотом», смог составить обычный военный бухгалтер.
        Вот в этом сила экселя, а не в том, что можно в две ячейки ввести цифры, а третья их тупо перемножит.
        • 0
          Вот в этом сила экселя, а не в том, что можно в две ячейки ввести цифры, а третья их тупо перемножит.

          Это я прекрасно понимаю. Но в то же время есть полно историй, когда бухгалтера столбцы на настольном калькуляторе суммируют )
          • +2
            Я даже лично знаю таких людей. И они делятся как минимум на две категории:
            — женщины за 60, которые отлично знают законодательную базу, но им тяжело с технологиями (именно они тянут всю бухгалтерию)
            — все остальные, которые учились на экономических факультетах, т.к. там конкурс меньше *

            * тут у нас проблемы в системе образования
            • 0
              Да не в образовании проблема, а в головах. Еще в школе преподают компьютерную грамотность, в том числе обучают работе в офисном пакете.
              • +2
                Знаете, в 8 классе я учился в негосударственной школе и нам на уроках информатики преподавали QBasic. Позже я переехал и 10-11 класс ('98-'00 годы) заканчивал в обычной московской школе. Там нам два года рассказывали что такое «манипулятор типа мышь» и как правильно выключать компьютер.
                • 0
                  У нас был QBASIC класса с пятого, 11-12 Visual Basic. Хотя школа была гуманитарная, просто повезло с учителем.
            • +1
              А ещё есть такие люди, которые полагают, что для сложения двух чисел калькулятор удобнее, чем запуск специальной программы. У меня был как-то калькулятор к клавиатуре приклеен для таких нужд.
              • +1
                Потому как это реально удобней. У меня и сейчас Citizen SRP145 всегда под рукой лежит.
                • +1
                  У меня ровно наоборот — с какого-то момента для абсолютно любых расчетов запускаются электронные таблицы или на худой кнец bc (это линуксовый консольный калькулятор — с переменными, функциями и т.д.). Потому что — история, простота просмотра и проверки того, что именно делал. А когда это понадобится — неизвестно. Сто раз было, что захочешь одну операцию с парой чисел сделать, а заканчивается жирной формулой.
                  • 0
                    А я когда надо что-то сложить/перемножить, но голова занята чем-то другим, запускаю интерпретатор питона (%
          • +5
            Иногда имеет смысл. Если эти столбцы предварительно заполняли не вы сами!
            С год назад читал историю, как один бухгалтер не повёлся на «Да зачем пересчитывать то? Видите, тут обычная формула =SUM по столбцу!» и таки пересчитал итоги выставленного счёта. И вот там последствия скрытой строки, в которую продавец-мошенник вписал лишнюю 1000р. и всплыли!
            • 0
              Еще чудесно когда где-то точка вместо запятой стоит (или наоборот)
          • +1
            Вы не поверите, но в селах до сих пор пользуется счетами
            • 0
              Бабушка-продавец из соседнего ларька с полгода назад-таки сменила счеты на калькулятор :(
              • 0
                Продвинутая у вас там бабулька! :)
    • +5
      Это просто показательный пример, что не нужно для решения простых задач подключать Jquery, ExtJs.
      • –2
        а почему бы и не подключить, если это упростит разработку и восприятие кода? Тем более при необходимости дальнейшего развития приложения…
  • 0
    Круто! Только все-таки плохо, что все ссылки — абсолютные. Я ничего не понимаю в JS, и понятно что это — не для практического применения, но по-моему относительные ссылки были бы не сложнее
    • 0
      Но уже было бы не 30 строк)
      • 0
        Ну 32.
  • +1
    Кажется, Вы только что перебили бизнес Microsoft-у. )
    • 0
      Прощай, Office 365!
  • 0
    Во-первых не без багов.
    Во-вторых можно улучшить.
    В третьих молодца!, но код что-то не радует глаз.
    • 0
      1) У вас все без багов?
      2) Ваш код уже нельзя улучшить?
      3) Ваш код радует глаз?
      Оо!

      Не цепляйтесь к мелочам, блин.
      Ясно что не для продакш. Просто вкусно и лаконично.
      • 0
        «Сперва добейся», да?
  • +5
    Спасибо, что перепостили с хакерньюс статью — теперь желательно объяснить, как оно всё работает.
  • +1
    Действительно круто!
    Не сразу понял откуда "@" в самой первой ячейке. Тоже довольно изящное решение, хоть и мелочь )
    • 0
      Полагаю, что отсюда

      var j = 0;
      String.fromCharCode("A".charCodeAt(0)+j-1);
      
  • 0
    не удобно вводить данные, энтер не в ячейке не обрабатывается, кто бы допилил?
  • +2
    Мощно!

    Вся магия в одной строке:
    with (DATA) return eval(value.substring(1));
    все остальное — обвесы…
    • –1
      Нет там никакой магии, код написал безобразно.
      Вот пример как это могло быть: jsbin.com/OxuqUNoX/7/edit
  • 0
    Воу-воу-оу, полегче!
    У службы 911 уже все линии заняли в предынфарктном состоянии разработчики MS Office.
  • 0
    Хабравчане, а вдруг кому не лень довести сей пример до практического, для использования на реальных продуктах? Больше проверок на корректность, относительный ссылки, запрет на всё кроме избранного списка операций, работа с Enter'ом (выше уже было, видел), экспорт/импорт файлов формата CSV, копирование строк, столбцов и областей?
    • +1
      Добавляется регулярочка в проверку

      var re = /^=(\(?([a-zA-Z]{1,10}[0-9]{1,10})\)?(\+|-|\*|\/)?)+$/;
      if (value.charAt(0) == "=" && value.search(new RegExp(re)) != -1 ) {}
      

      Профит!

      ЗЫ Даже первое условие можно удалить, оставив только регулярку
      • +2
        Чтобы оставить количество строк равное 30 в скрипте

        if (value.search(new RegExp(/^=(\(?([a-zA-Z]{1,10}[0-9]{1,10})\)?(\+|-|\*|\/)?)+$/)) != -1 ) {}
        

        :D
        • 0
          почему не написать /^...$/.test(value)?
          • +1
            Ну наверное потому что
            test:
            Используется, чтобы выяснить, есть ли совпадения регулярного выражения со строкой, аналогично String#search.

            search:
            Этот метод удобен, когда нужно проверить, есть ли совпадения с регулярным выражением (аналогично RegExp#test).

            Т.е. без разницы.

            Или есть аргументы непосредственно в пользу test?
        • 0
          Разве вы таким образом не срежете всё, кроме обыкновенной арифметики?

          В то время как в этом «Экселе» в изначальном виде может работать что-нибудь посложнее, как к примеру «Math.sqrt()».

          Если уж урезать, то ценные вещи нужно оставлять. А то у него функционала остаётся меньше чем у обычного калькулятора.
          • 0
            Разве вы таким образом не срежете всё, кроме обыкновенной арифметики?

            Ну читая комменты я понял, что другое и не поддерживается (не должно).
            Если поддерживается что-то ещё, то можно добавить.

            В то время как в этом «Экселе» в изначальном виде может работать что-нибудь посложнее, как к примеру «Math.sqrt()».

            Хм, это я не видел. Можно и это добавить.
            Готов помочь в этом. Но надо примеры, под которые подстраиваться.
            • 0
              Нужно собрать весь список математических операций JS,,, Тогда уж легче и правильнее делать просто whitelist команд.
  • 0
    Ничего более полезного в ячейку не додумался записать…

    = for(i=A1; i<B1; i++){console.log(i)}
  • 0
    Был такой проект от Tidestone — F1Report. что то типа light Excel. Никто его исходников не имеет? А такие же легкие аналоги не знает?
  • 0
    Реквестирую 1С в 50 строк!
  • 0
    в между тем этот эксель уже ужали до 328 байт

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