Кажется, ваш HTML недействителен HTML. Spe c не определяет такие элементы, как <invoice>
, <headers>
и <item>
. Кроме того, атрибуты на элементах почти всегда напоминают пары ключ-значение, то есть вы должны объявить свои атрибуты name
, buyer
, order
, quantity
и rate
в качестве значений существующих атрибутов. Атрибут contenteditable
является логическим атрибутом, который можно оставить без изменений.
Вот исправленный и рабочий пример:
var button = $('#read-invoice');
// readLine :: [String] -> (HTMLElement -> String)
function readLine(fields) {
return function (el) {
return fields.reduce(function (txt, field) {
var data = $('.' + field, el).text();
return txt === ''
? field + ': ' + data
: txt + '; ' + field + ': ' + data
}, '');
}
}
// readBlock :: { (HTMLElement -> String) } -> (HTMLElement -> String)
function readBlock(readers) {
return function (el) {
var rtype = el.className;
if (typeof readers[rtype] === 'function') {
return readers[rtype](el);
}
return '';
}
}
// autoRead :: HTMLElement -> String
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate'])
// ... address, etc.
});
button.on('click', function () {
var result = $('.invoice').
children().
toArray().
reduce(function (txt, el) {
var line = autoRead(el);
return line === ''
? txt
: txt + line + '\n';
}, '');
console.log(result);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="invoice">
<div class="headers">
<div class="date" contenteditable>15-Jan-2020</div>
<div class="buyer" contenteditable>McDonalds</div>
<div class="order" contenteditable>145632</div>
</div>
<div class="item">
<div class="name" contenteditable>Big Mac</div>
<div class="quantity" contenteditable>5</div>
<div class="rate" contenteditable>20.00</div>
</div>
<div class="item">
<div class="name" contenteditable>Small Mac</div>
<div class="quantity" contenteditable>10</div>
<div class="rate" contenteditable>10.00</div>
</div>
</div>
<button id="read-invoice">Loop</button>
JS объяснение
Функция readLine
принимает Array
String
с, где каждый String
напоминает имя класса одного из внутренних <div>
элементов. Он возвращает функцию, которая ожидает элемент «блок» (например, <div class="headers">
) и считывает содержимое содержащихся в нем <div>
в один String
. Давайте назовем возвращенную функцию reader .
Функция readBlock
берет Object
функций считывателя и возвращает функцию, содержащую элемент "block". Возвращаемая функция определяет, какой тип «блока» она получила, и вызывает соответствующую функцию чтения с элементом в качестве аргумента. Если ни один читатель не соответствует типу блока, он возвращает пустое значение String
.
. В конце концов, autoRead
становится единственной функцией, принимающей целый элемент «блока» и возвращающей все его содержимое в виде строки. текста.
Обработчик клика button
ищет элемент <div class="invoice">
, пересекает его дерево DOM до его дочерних элементов (наших элементов "блока") и передает каждый "блок" в autoRead
, наращивание результата String
. Окончательный результат заносится в консоль.
Расширение
Чтобы добавить новые типы «блоков», просто определите для них новый считыватель и добавьте его в Object
передано readBlock
. Например, добавить читатель <div class="address">
, который читает информацию "name", "street", "zip" и "city":
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate']),
address: readLine(['name', 'street', 'zip', 'city']) // <<< new
});
Расширить поля, которые читает определенный читатель, также просто, просто добавить название поля следующего содержания:
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate', 'currency']) // <<< added "currency"
});