Вы можете подумать об изменении ваших исходных данных, и таким образом вы можете просто использовать одну привязку данных и один шаблон обновления. В рамках этого паттерна вы можете проверять / использовать слово, а также проверять каждую букву слова (вы также можете сделать это на лету позже):
const test_data = [ 'hello', 'world', 'wthtvwls' ];
const modified_data = test_data.map((item,index) => {
return {
word: item,
array: Array.from(item)
}
});
console.log(modified_data);
Затем, используя эти данные (демо - https://codepen.io/Alexander9111/pen/qBdZrLv):
svg = d3.select('body')
.append('svg')
.attr('width', 640)
.attr('height', 480);
svg.selectAll('text')
.data( modified_data )
.enter()
.append('text')
.attr('x', 10)
.attr('y', function( d, i ) {
return 20 + i * 20;
})
.html(function(d) {
return d.letters.map((letter,index) => {
if (['a', 'e', 'i', 'o', 'u', 'y'].includes(letter)){
return '<tspan class="vowel">' + letter + '</tspan>';
} else{
return '<tspan>' + letter + '</tspan>';
}
}).join("");
})
.attr('class', function( d, i ) {
for (letter of d.letters){
console.log('letter', letter);
if (['a', 'e', 'i', 'o', 'u', 'y'].includes(letter)) {
return 'strong';
}
}
})
.exit();
Обратите внимание на использование функции .html()
, а затем .map()
в ней, используя наши текущие data.letters в обычном шаблоне обновления, чтобы добавить либо '<tspan class="vowel">'
или '<tspan>'
(без гласного класса).
https://codepen.io/Alexander9111/pen/qBdZrLv
Но я не уверен, хотите ли вы такой конкретный c вариант использования или более общий ответ на вложенный шаблон обновления? Если так, то, возможно, этот блок помогает? - https://bl.ocks.org/mpbastos/bd1d6763d34ac5d3ce533a581b291364
ОБНОВЛЕНИЕ - я понимаю, что неправильно прочитал вопрос в первый раз
Так что теперь мне ясно, что мой временное решение не более полезно, чем два других решения, предложенных @Fabien (OP) (перемещение строки вверх (перед вторым data ()) или использование отдельного оператора svg.selectAll('text').attr('class', 'strong');
)
Я пробовал такие шаблоны как:
svg.selectAll('text')
.data( test_data )
.enter()
.append('text')
.attr('x', 10)
...
.selectAll('tspan')
.data( ...)
.enter()
.append('tspan')
.attr('class', 'example')
.exit()
.select(this.ParentNode)
.attr('class', 'example')
А также с .select(this.ParentNode)
до .exit()
, но не сработало.
Единственное, что работает, но это также обходной путь и, вероятно, анти-шаблон вот что - перехитрите функцию attr, чтобы "захватить" родительский объект за пределами вложенного обновления:
svg = d3.select('body')
.append('svg')
.attr('width', 640)
.attr('height', 480);
text = svg.selectAll('text')
.data( test_data )
.enter()
.append('text')
.attr('x', 10)
.attr('y', function( d, i ) {
return 20 + i * 20;
})
.selectAll('tspan')
.data( function( d, i ) { // d is from the first data
return Array.from(d); // if needed, could return an array or object that includes the initial value, too.
})
.enter()
.append('tspan')
.attr('class', function( d, i ) {
console.log(1, i, this.parentNode);
//hijack the attr function to "grab" parent outside nested update pattern
if (i == 0) {
d3.select(this.parentNode).attr('class', 'strong');
}
//console.log( 'tspan class:', d, i );
if ( ['a', 'e', 'i', 'o', 'u', 'y'].includes(d) ) {
return 'vowel';
}
})
.text( function( d, i, foo ) { // d is from the second data
//console.log( 'tspan text:', d, i, /*foo*/ );
return d;
})
.exit();
Это также не очень хорошее решение.
Я думаю, потому что когда-то вызывается функция .data()
при выборе теперь он больше обычного объекта списка узлов, а функция .exit()
предназначена для использования только на один уровень глубины.
Я думаю, что два решения, изложенные в ОП, являются лучшими, но я хотел бы оказаться неправым!