【🎄Christmas Sale 10% OFF Code: christmas25】Buy 4 Get 1 Free + Free shipping for order $59
NEW
New in Stamped Cross Stitch
New in Counted Cross Stitch
New in Animal Cross Stitch
New in Flower Cross Stitch
New in Scenery Cross Stitch
New in Original Brand
New in Large Size
New in Small Size
New in Cross Stitch Crafts
New in Silk Thread Cross Stitch
New in Egyptian Cotton Cross Stitch
New in Bundle Sale
New in Cross Stitch Tools
HOT
Customized
Clearance
Bundle Sale
Best price
Clearance
Under $10
Under $20
Collections
Brand
Stamped Cross Stitch
Popular series
ALL Cross Stitch
Animal
Abstract
Cartoon
Flower
Scenery
Character
Festival
Counted Cross Stitch
11CT Counted
14CT Counted
18CT Counted
Cross Stitch Crafts
Bead Embroidery
Needle Embroidery
Latch Hook Kits
New~~Cross Stitch Bag
Cross stitch Pillowcase
Bead Embroidery
Cross stitch bookmark
Cross Stitch Gifts
Large Size
Shop by Size
Shop by Collections
Shop by Price
5Pcs Multi-Size
Normal Size
Accessories
Frame Hoop
Finger protection tool
Thread & Thread Holder
Scissors
More Tools>>
US Warehouse
NEW
New in Stamped Cross Stitch
New in Counted Cross Stitch
New in Animal Cross Stitch
New in Flower Cross Stitch
New in Scenery Cross Stitch
New in Original Brand
New in Large Size
New in Small Size
New in Cross Stitch Crafts
New in Silk Thread Cross Stitch
New in Egyptian Cotton Cross Stitch
New in Bundle Sale
New in Cross Stitch Tools
HOT
Customized
Clearance
Bundle Sale
Best price
Clearance
Under $10
Under $20
Collections
Brand
Hot
Stamped Cross Stitch
Popular series
Hot selling series>>
Silhouette Series
Lighthouse & Castle
Doll Series
Ginkgo Series
Four Seasons
Flower Birds
World Famous
Month Jan. - Dec.
Retro Poster
Tai Chi
Love Style
Famous painting
Glass Painting
Sewing Art
Color Art
ALL Cross Stitch
Silk thread (Distinctive shine)
Egyptian cotton thread (Fade-resistant and high-quality)
Eco-friendly cotton thread (Most popular and cost-effective)
9CT Stamped
11CT Stamped
14CT Stamped
16CT Stamped
18CT Stamped
30*40CM
40*40CM
40*50CM
40*55CM
40*60CM
50*70CM
Animal
Cat
Dog
Wolf
Elephant
Butterfly
Dragon
Peacock
Bird
Bear
Fish
Deer
Cow
Tiger
Rabbit
Owl & Eagle
Horse
Sheep & Goat
Abstract
Letter
Flag
Bottle
Cake&Coffee
Sewing Art
Book Art
Candle Art
Watercolor Series
Music Art
Color Art
Blue and White Porcelain
Gustav Klimt style
Pop Art
Rugby Series
Quirky Series
Patchwork Series
Cartoon
Lord Of The Rings
The Wizard of Oz
Demon Slayer
Dragon Ball
Sailor Moon
One Piece
Yoda
Princess
Mario
Stitch
Hayao Miyazaki
Flower
Rose
Sunflower
Lotus
Peony
Tulip
Lily
Lavender
Iris
Hydrangea
Pansy
Scenery
Castle
House
Moon
Sea
River
Beach
Vehicle
Tree
Aurora
Character
Noble Lady
Beauty Girl
Santa Claus
Angel
Gnome
Snowman
Victorian
Granny
Quirky Series
Festival
Religion
Christmas
Halloween
Valentine's Day
Thanksgiving Day
Easter
Counted Cross Stitch
11CT Counted
14CT Counted
18CT Counted
Cross Stitch Crafts
Bead Embroidery
Needle Embroidery
Latch Hook Kits
New~~Cross Stitch Bag
Cross stitch Pillowcase
Bead Embroidery
Cross stitch bookmark
Cross Stitch Gifts
Large Size
Shop by Size
Over 60cm(23.62inches)
Over 70cm(27.56inches)
Over 80cm(31.5inches)
Over 90cm(35.43inches)
Over 100cm(39.37inches)
Over 140cm(55.12inches)
Shop by Collections
Big Size-Animals
Big Size-Flowers
Big Size-Landscape
Big Size-Cartoon
Big Size-Anime
Big Size-Character
Big Size-Christmas
Big Size-Religion
Big Size-Abstract
Shop by Price
$17.99 Zone
$18.99 Zone
$19.99 Zone
$20.99 Zone
$21.99 Zone
$22.99 Zone
5Pcs Multi-Size
Normal Size
Accessories
Frame Hoop
Finger protection tool
Thread & Thread Holder
Scissors
More Tools>>
US Warehouse
Login
Register
Login
Register
HOT SALE
33%
OFF
Tools
【Wholesale Discount】Thimble - MUST HAVE Embroidery Cross Stitch Sewing Craft Tools
tapestrymarket
$1.99
$2.99
40%
OFF
Tools
【Wholesale Discount】30pcs Cross Stitch Sewing Needle Threading Device DIY Tool Needle Threader
tapestrymarket
$2.99
$4.99
17%
OFF
(Counted/Stamped) 11CT/14CT Flower Home - Cross Stitch 50*40cm/19.69*15.75in
tapestrymarket
$9.99
$11.99
17%
OFF
(Counted/Stamped) 11CT/14CT Family A Whole Lot of Love Quotes - Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$9.99
$11.99
19%
OFF
Halloween-14CT Stamped Cross Stitch 38*31cm/14.96*12.2in
tapestrymarket
$12.99
$15.99
39%
OFF
Snowman-11CT/14CT/18CT Stamped/Counted Cross Stitch 45*45cm
tapestrymarket
$13.99
$22.99
6%
OFF
(Stamped/Counted) Cardinal Bird- 11CT Cross Stitch 40*40cm
tapestrymarket
$14.99
$16.99
Christmas Tree Truck-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
29%
OFF
Christmas Tree-14CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
$20.99
Cat And Bird-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
14%
OFF
Highland Cow-14CT/18CT Stamped/Counted Cross Stitch 40*45cm/15.75*17.72in
tapestrymarket
$18.99
$21.99
Sewing Machine-14CT/18CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$19.99
$24.99
Colorful Mosaic Pumpkin-14CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$17.99
8pcs Christmas Snowman-11CT Stamped Cross Stitch Set 12.5*12.5cm/4.92*4.92in
tapestrymarket
$19.99
29%
OFF
Bottle Faith Hope Love-11CT Stamped/Counted Cross Stitch 45*45cm
tapestrymarket
$14.99
$20.99
7%
OFF
(Multi-Style)Dragon Glass Painting - 11CT/14CT Stamped Cross Stitch (Multi-Size)(glass painting cross stitch)
tapestrymarket
$12.99
$13.99
25%
OFF
(Stamped/Counted) Gnome By Window In Winter-11CT/14CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$11.99
$15.99
13%
OFF
(Counted/Stamped) 11CT/14CT Bird House - Cross Stitch 30*40cm/11.81*15.75in
tapestrymarket
$13.99
$15.99
33%
OFF
(Stamped/Counted) Blue And White Porcelain Vase-11CT Cross Stitch 45*45cm
tapestrymarket
$13.99
$20.99
3%
OFF
STITCH YOUR PHOTO (Create Your Own Unique Custom cross stitch)
tapestrymarket
$35.99
$36.99
View all
New Arrivals
Christmas Tree-11CT Stamped Cross Stitch 40*40cm
tapestrymarket
$13.99
21%
OFF
Cat By Window-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
$18.99
(Multi-Style) Watercolor Dog-11CT Stamped Cross Stitch 40*50cm/15.75*19.69in
tapestrymarket
$19.99
Coffee Kitten-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$18.99
17%
OFF
Christmas Gnome-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
$17.99
Christmas Cottage-14CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$19.99
21%
OFF
Santa Claus-11CT Stamped/Counted Cross Stitch 40*40cm
tapestrymarket
$14.99
$18.99
13%
OFF
Christmas Forest-14CT Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$20.99
$23.99
17%
OFF
Cross-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$19.99
$23.99
Sewing Shop-14CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$19.99
Cat-11CT Stamped Cross Stitch 40*55cm
tapestrymarket
$18.99
Dragon Book-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$18.99
Colorful Waterfall-11CT Stamped Cross Stitch 50*40cm
tapestrymarket
$18.99
Holy Night-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$18.99
Snowman-11CT Stamped Cross Stitch 50*40cm
tapestrymarket
$18.99
Dragon-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$18.99
Independence Day Truck-11CT Stamped Cross Stitch 40*40cm
tapestrymarket
$14.99
Sun Moon-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$18.99
Christmas Highland Cow-11CT Stamped Cross Stitch 40*40cm
tapestrymarket
$14.99
Wizard Gnome-11CT Stamped Cross Stitch 40*50cm
tapestrymarket
$18.99
View all
Hot Collections
You May Like
17%
OFF
(Counted/Stamped) 11CT/18CT Peony Flower - Cross Stitch -40x50cm/15.75*19.69in
tapestrymarket
$9.99
$11.99
13%
OFF
(Counted/Stamped)(Big Size) 11CT Ginkgo biloba- Cross Stitch 60*60cm/23.62*23.62in
tapestrymarket
$13.99
$15.99
(Stamped/Counted) Cat Scenery-11CT Cross Stitch 40*40cm
tapestrymarket
$14.99
Nativity-14CT Counted/Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$19.99
(Stamped/Counted) Christmas Snow Scene By Window-11CT Cross Stitch 45*45cm/17.72*17.72in
tapestrymarket
$21.99
Bookstore-11CT Stamped/Counted Cross Stitch 50*50cm/19.69*19.69in
tapestrymarket
$24.99
Witch Hat-11CT Stamped/Counted Cross Stitch 40*40cm
tapestrymarket
$15.99
15%
OFF
Original Brand
(Counted/Stamped) 11CT Blue Cat-Cross Stitch-36x50CM/14.17*19.69in(Brand)
tapestrymarket
$16.99
$19.99
(Counted/Stamped)(Big Size) 11CT Sunflower- Cross Stitch 50*65cm/19.69*25.59in
tapestrymarket
$20.99
(Stamped/Counted) Blue And White Porcelain Cat-11CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$17.99
$21.99
19%
OFF
(Stamped/Counted) Vintage Poster Horse And Carriage - 11CT/14CT Stamped/Counted Cross Stitch 40*60cm/15.75*23.62in(Poster Cross stitch)
tapestrymarket
$16.99
$20.99
6%
OFF
(Big Size)Flower butterfly in the window-11CT/14CT Stamped Cross Stitch 50*60cm/19.69*23.62in
tapestrymarket
$28.99
$30.99
14%
OFF
Original Brand
(Multi-Style)Angel-14CT Stamped Cross Stitch(Brand)
tapestrymarket
$17.99
$20.99
12%
OFF
Beach Dress Girl-11CT/14CT/18CT Stamped/Counted Cross Stitch 45*45cm
tapestrymarket
$21.99
$24.99
13%
OFF
(Multi-Style) Scenery - 11CT Stamped Cross Stitch (Multi-Size)
tapestrymarket
$13.99
$16.99
20%
OFF
Original Brand
(Counted/Stamped) 11CT Yoda - Cross Stitch Kit 36*46cm/14.17*18.11in(Brand)
tapestrymarket
$15.99
$19.99
13%
OFF
(Stamped/Counted) Freddie Mercury - 11CT Cross Stitch 40*50cm/15.75*19.69in
tapestrymarket
$20.99
$23.99
20%
OFF
(Counted/Stamped) 11CT Every Day Is A New Beginning Sunflowers Quotes - Cross Stitch 50*40cm/19.69*15.75in
tapestrymarket
$15.99
$19.99
10%
OFF
Original Brand
11CT Stamped Van Gogh'S Starry Sky - Cross Stitch 59*45cm/23.23*17.72in(Brand)
tapestrymarket
$27.99
$30.99
6%
OFF
(Counted/Stamped) 11CT Princess Belle-Cross Stitch-40*50cm/15.7x19.7in~Silhouette cross stitch
tapestrymarket
$16.99
$17.99
View all
Multi-Style / Hot Selling Series
50%
OFF
Original Brand
(Multi-Style) Twelve Months Flower-14CT Counted Cross Stitch 17*17cm
$5.99
$12.99
20%
OFF
(Multi-Style) Animal - 18CT Stamped Cross Stitch 20*20cm/7.87*7.87in
tapestrymarket
$7.99
$9.99
17%
OFF
(Multi-Style) Goblin Goes To Toilet-18CT Stamped Cross Stitch 25*25cm/9.84*9.84in
$12.99
$15.99
25%
OFF
(Multi-Style) Football-11CT Stamped Cross Stitch 40*40cm
tapestrymarket
$16.39
$21.99
(Multi-Style)Luminous Vase-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$17.99
(Multi-Style)Window Bicycle Flowers-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
(Multi-Style)Halloween Princess Witch-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
(Multi-Style)Christmas Snowman-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
(Multi-Style) Blue And White Porcelain-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
$18.99
16%
OFF
(Multi-Style) Victorian Flower Heart - 11CT Stamped Cross Stitch -50*50CM
tapestrymarket
$15.99
$18.99
17%
OFF
(Multi-Style) Stack Of Books With Flowers - 11CT Stamped Cross Stitch 30*80cm/11.81*31.5in(Big Size)
$20.99
$25.19
20%
OFF
(Multi-Style) Abstract Art Vase - 11CT Stamped Cross Stitch 50*50cm
$19.99
$24.99
(Multi-Style) Colorful Houses-14CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
$19.99
(Multi-Style)Winter Scenery-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
12%
OFF
(Multi-Style) Landscape-11CT Stamped Cross Stitch 45*45/50*50cm
$21.99
$26.39
4%
OFF
(Multi-Style) Blue And White Porcelain-14CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$22.99
$23.99
(Multi-Style) Sewing Shop-14CT Counted Cross Stitch 40*40cm/15.75*15.75in
$19.99
4%
OFF
(Multi-Style) Witch-11CT Stamped Cross Stitch 45*45cm/17.72*17.72in
tapestrymarket
$21.99
$22.99
7%
OFF
(Multi-Style) Halloween Skeleton-11CT Stamped Cross Stitch 45*65cm/50*65cm(Big Size)
tapestrymarket
$27.99
$29.99
33%
OFF
(Multi-Style)Silhouette Series - 11CT Stamped Cross Stitch 40*50cm/15.7x19.7in
tapestrymarket
$10.99
$16.99
View all
For Beginner cross stitch
46%
OFF
Original Brand
(Counted/Stamped) 11CT/14CT Religion - Cross Stitch 22*17cm/8.66*6.69in(Brand)
tapestrymarket
$6.99
$15.99
46%
OFF
Original Brand
(Counted/Stamped) 14CT Sunflower Vase - Cross Stitch 19*26cm/7.48*10.24in(Brand)
tapestrymarket
$6.99
$12.99
17%
OFF
Original Brand
(Counted/Stamped) 11CT/14CT Wolf - Cross Stitch 21*30cm/8.27*11.81in(Brand)
tapestrymarket
$7.99
$9.59
15%
OFF
Original Brand
(Stamped/Counted ) 4pcs Four Seasons - 14CT Cross Stitch 27*26cm/10.63*10.24in(Brand)
tapestrymarket
$10.99
$12.99
29%
OFF
Original Brand
(Counted/Stamped) 11CT/14CT Cheerful Christmas - Cross Stitch 30*21cm/11.81*8.27in(Brand)
tapestrymarket
$9.99
$13.99
8%
OFF
Original Brand
(Counted/Stamped) Merry Christmas-14CT Cross Stitch 32*45cm/12.6*17.72in(Brand)
tapestrymarket
$10.99
$11.99
53%
OFF
Original Brand
(Counted/Stamped) 14CT Embrace - Cross Stitch 28*31cm/11.02*12.2in(Brand)
tapestrymarket
$7.99
$16.99
19%
OFF
Original Brand
(Counted/Stamped) 11CT/14CT lighthouse - Cross Stitch 30*40cm/11.81*15.75in(Brand)
tapestrymarket
$12.99
$15.99
16%
OFF
Original Brand
(Counted/Stamped) 11CT Stitch - Cross Stitch 46*36cm/18.11*14.17in(Brand)
tapestrymarket
$15.99
$18.99
14%
OFF
Original Brand
(Big Size) 14CT Stamped Christmas tree - Cross Stitch 48*66cm/18.9*25.98in(Brand)
tapestrymarket
$24.99
$28.99
View all
Big Size(High Level-14CT/16CT/18CT)
10%
OFF
(Counted/Stamped) 11CT/14CT/16CT/18CT Rivendell The Lord of the Rings - Cross Stitch 40*85cm/15.75*33.46in(Big Size)
tapestrymarket
$18.99
$20.99
23%
OFF
Original Brand
The Last Supper -14CT Stamped Cross Stitch -77*35CM(Brand)
tapestrymarket
$22.99
$29.99
14%
OFF
(Counted/Stamped)(Big Size) 11CT Family - Cross Stitch 90*30cm/35.43*11.81in
tapestrymarket
$18.99
$21.99
3%
OFF
(Counted/Stamped) 11CT/18CT Cross Stitch-Waterfall Scenic Landscape 40*85cm/15.75*33.46in(Big Size)
tapestrymarket
$27.99
$28.99
18%
OFF
(Counted/Stamped) 9CT/11CT Flower - Cross Stitch(Multi-Size)
tapestrymarket
$22.99
$27.99
10%
OFF
(Big Size) Flying Dragon - 11CT Stamped/Counted Cross Stitch 60*45cm/23.62*17.72in(glass painting cross stitch)
tapestrymarket
$25.99
$28.99
20%
OFF
(Counted/Stamped) 11CT Mountain River - Cross Stitch 100*45cm/39.37*17.72in
tapestrymarket
$31.99
$39.99
(Counted/Stamped)(Big Size) 11CT Sunflower- Cross Stitch 50*65cm/19.69*25.59in
tapestrymarket
$20.99
7%
OFF
(Counted/Stamped) 11CT/18CT Hot Air Balloons/Castle - Cross Stitch(Big Size)
tapestrymarket
$25.99
$27.99
19%
OFF
(Stamped/Counted) Painted Trees-11CT Cross Stitch 66x40cm(Big Size)
tapestrymarket
$21.99
$26.99
View all
Counted Cross stitch
38%
OFF
Piano Girl-11CT/14CT/18CT Stamped/Counted Cross Stitch 40*40cm
tapestrymarket
$14.99
$23.99
19%
OFF
Peter Rabbit-11CT Stamped/Counted Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
$20.99
22%
OFF
Christmas Wreath And Pine Cones-11CT Counted Cross Stitch 40*40cm
tapestrymarket
$13.99
$17.99
17%
OFF
(Stamped/Counted) Sewing Machine Girl-11CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$14.99
$17.99
25%
OFF
Love Shell Beach-11CT/14CT Stamped/Counted Cross Stitch 40*40cm
tapestrymarket
$14.99
$19.99
(Stamped/Counted) Boy And Puppy-11CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$18.99
(Stamped/Counted) Flower Candle Lights-11CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$15.99
28%
OFF
(Stamped/Counted) Steampunk-11CT Cross Stitch 50*50cm/19.69*19.69in
tapestrymarket
$17.99
$24.99
(Stamped/Counted) Guitar Landscape Silhouette-11CT/14CT Cross Stitch 40*40cm
tapestrymarket
$18.99
14%
OFF
(Stamped/Counted) Halloween Pumpkin Street-11CT Cross Stitch 40*40cm
tapestrymarket
$18.99
$21.99
View all
Hot Cartoon
Bead embroidery
2Pcs Mickey Minnie - Stamped Bead Embroidery - Keychain
tapestrymarket
$12.99
27%
OFF
(Stamped/Counted) Disney Stitch Flower - 11CT Cross Stitch 40*40cm
tapestrymarket
$15.99
$21.99
19%
OFF
(Stamped/Counted) Beauty and the Beast-11CT Cross Stitch 40*40cm
tapestrymarket
$16.99
$20.99
(Stamped/Counted)Silhouette Stitch-11CT/14CT Cross Stitch 50*50cm/19.69*19.69in
tapestrymarket
$17.99
My Neighbor Totoro-11CT Stamped Cross Stitch 45*45cm/17.72*17.72in
tapestrymarket
$20.99
Castle Mickey Avatar-11CT Stamped Cross Stitch 45*45cm
tapestrymarket
$19.99
(Stamped/Counted) Sewing Mickey-11CT Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$16.99
Mickey Reading A Book-11CT Stamped Cross Stitch 40*40cm/15.75*15.75in
tapestrymarket
$13.99
11%
OFF
Disney Princess Castle-11CT Stamped Cross Stitch 50*50cm
$24.99
$27.99
12%
OFF
Mickey Minnie Disney-11CT Stamped Cross Stitch 45*45cm
$21.99
$24.99
View all
Cross stitch & Crafts
DIY Embroidery Cross stitch Crafts
55%
OFF
Bead embroidery
Totoro - Stamped Bead Embroidery - Keychain
tapestrymarket
$5.99
$13.19
14%
OFF
11CT DIY Stamped Cross Stitch Shopping Bag 40*40cm/15.75*15.75in
tapestrymarket
$18.12
$21.12
Flower Hummingbirds-DIY Embroidery Kit with Pattern Cotton Threads Needles Hoop 30*30cm/11.8*11.8in
$13.99
19%
OFF
(No Printed Canvas)Cross Stitch Bookmark Cotton Thread 14CT Counted DIY Literature Art XJL012
tapestrymarket
$12.99
$15.99
13%
OFF
(No Printed Canvas)14CT Still Life Cross Stitch Bookmark Double Side Counted Tassel (XJL069)
tapestrymarket
$12.99
$14.99
5Pcs Christmas Tree - Cross Stitch Wood Pendants Embroidery 10cm
tapestrymarket
$9.99
Bead embroidery
Butterfly Sequins Embroidery Beaded Brooch Handmade DIY Brooch Material Kit
tapestrymarket
$14.99
11%
OFF
Bead embroidery
Thumbelina DIY Cross Stitch Kit 14CT Thread Art Needlework Fridge Magnets 7x11cm
tapestrymarket
$15.99
$17.99
18%
OFF
(Multi-Style) Tiger-DIY Embroidery Kit with Pattern Cotton Threads Needles Hoop 30*30cm/11.81*11.81in
$13.99
$16.99
24%
OFF
(Multi-Style) Christmas-DIY Embroidery Kit with Pattern Cotton Threads Needles Hoop 25*25cm/9.84*9.84in
$12.99
$16.99
View all
Cross Stitch Accessories
33%
OFF
Tools
【Wholesale Discount】Thimble - MUST HAVE Embroidery Cross Stitch Sewing Craft Tools
tapestrymarket
$1.99
$2.99
20%
OFF
Tools
【Wholesale Discount】Cross Stitch Embroidery Needle Insert Bag DIY Sewing Knit Thread Pin Pouch
tapestrymarket
$7.99
$9.99
25%
OFF
Tools
50pcs Cross stitch Sewing Needles Embroidery Stitching Big Eye Needle
tapestrymarket
$2.99
$3.99
17%
OFF
Tools
Square Shape Frame Hoop - Cross Stitch Accessories
tapestrymarket
$9.99
$11.99
13%
OFF
Tools
Original Brand
【Wholesale Discount】Water-soluble Embroidery Thread Beeswax Block with Box DIY Cross Stitch Wax(Brand)
tapestrymarket
$6.99
$7.99
33%
OFF
Tools
Cross Stitch Finger Protection Cover Embroidery bandage
tapestrymarket
$3.99
$5.99
14%
OFF
Tools
【Wholesale Discount】Cross Stitch Tool Clip
tapestrymarket
$5.99
$6.99
Tools
Original Brand
【Wholesale Discount】30-hole Sewing Tool Kit Cross Stitch Row Line Floss Thread Holder(Brand)
tapestrymarket
$8.99
50%
OFF
Tools
【Wholesale Discount】Cross Stitch Thread Plate Clothes Hanger Sewing Tools Accessories(Random Color)
tapestrymarket
$4.99
$9.99
6%
OFF
Tools
【Wholesale Discount】Desktop Solid Wood Cross Stitch Frame Embroidery Canopy Height Adjustable
tapestrymarket
$58.99
$62.99
View all
Blogs
View all
let section_id = '1638170143953'; window.reviewSettings = {}; window.reviewSettings[section_id] = { "sub_title": "", "star_least": "4", "only_featured": false, "with_photo": true, "review_insufficient": "no_reviews", "minimum_comment_num": 5, "fill_strategy": "hide", "layout": "grid", "image_size": "natural", "wall_mobile_num": 2, "wall_pc_num": 4, "limit": 12, "show_product": true, "hide_review_section": true, "title": "5-Star Photo Reviews", "accent_color": null, "color_title": "#000000", "text_color": "#000000", "card_wrap_color": null, "background_color": "#ffffff" };
const TAG = 'spz-custom-revue-util'; const DEFAULT_DELAY_TIME = 100; class SpzCustomRevueUtil extends SPZ.BaseElement { constructor(element) { super(element); this.templates_ = SPZServices.templatesForDoc(); } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); } static deferredMount() { return false; } mountCallback() { } debounceRender(el, thisEl, containerStr) { return this.smoothRender_(el, thisEl, containerStr).then(() => this.attemptToFit_(thisEl)); } smoothRender_(newEl, thisEl, containerStr) { const that = this; that.appendAsUnvisibleContainer_(newEl, thisEl); const components = newEl.querySelectorAll('[layout]'); return Promise.race([ Promise.all( Array.prototype.map.call(components, (e) => SPZ.whenDefined(e).then(() => e.whenBuilt()) ) ), SPZServices.timerFor(that.win).promise(DEFAULT_DELAY_TIME), ]).then(() => { return containerStr !== 'form_' ? thisEl.mutateElement(() => that.quickReplace(thisEl, newEl)) : thisEl.mutateElement(() => that.quickReplaceForm(thisEl, newEl)); }); } quickReplace(thisEl, newEl) { thisEl.container_ && this.toggleVisible_(thisEl.container_); this.toggleVisible_(newEl, true); thisEl.container_ && SPZCore.Dom.removeElement(thisEl.container_); thisEl.container_ = newEl; }; quickReplaceForm(thisEl, newEl) { thisEl.form_ && this.toggleVisible_(thisEl.form_); this.toggleVisible_(newEl, true); const children = thisEl.form_.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.toggleVisible_(thisEl.form_, true); thisEl.form_.appendChild(newEl); }; appendAsUnvisibleContainer_(el, thisEl) { this.toggleVisible_(el); thisEl.element.appendChild(el); } attemptToFit_(thisEl) { const fitFunc = () => { thisEl.mutateElement(this.setElementHeight_.bind(thisEl)); }; const container = thisEl.container_ || thisEl.form_; if (container) { const children = container.querySelectorAll('*:not(template)'); const spzChildren = Array.prototype.filter .call(children, SPZUtils.isSpzElement) .filter((e) => !(e.isMount && e.isMount())); spzChildren .map((e) => SPZ.whenDefined(e).then(() => e.whenMounted())) .forEach((p) => p.then(() => fitFunc())); } return fitFunc(); } setElementHeight_() { const targetHeight = (this.container_ || this.form_)?./*OK*/ scrollHeight; const height = this.element./*OK*/ offsetHeight; if (height !== targetHeight) { SPZCore.Dom.setStyles(this.element, { height: `${targetHeight}px`, }); } } toggleVisible_(el, visible = false) { if (!visible) { el.classList.add('i-spzhtml-layout-fill'); SPZCore.Dom.setStyles(el, { 'z-index': -100000, 'opacity': 0, }); } else { el.classList.remove('i-spzhtml-layout-fill'); SPZCore.Dom.setStyles(el, { 'z-index': 'auto', 'opacity': 1, }); } } setMinWidth_() { const targetWidth = this.container_?./*OK*/ scrollWidth; const width = this.element./*OK*/ offsetWidth; if (width !== targetWidth) { SPZCore.Dom.setStyles(this.element, { 'min-width': `${targetWidth}px`, }); } } triggerEvent_ = (name, data) => { const event = SPZUtils.Event.create(this.win, `${TAG}.${name}`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomRevueUtil);
const TAG = 'spz-custom-revue-render'; class SPZCustomRevueRender extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); } mountCallback = () => {} render = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { if (this.element.children.length > 0) { this.element.children[0].style.display = 'none'; } this.element.appendChild(el); // const utilsEl = document.getElementById('spz_custom_revue_util'); // utilsEl && SPZ.whenApiDefined(utilsEl).then((api) => { // api.debounceRender(el, this); // }); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueRender)
${function(){ return `
${data.starNum}
/
${data.starTotal}
`; }()}
${function(){ return `
${data.showStarText === 'true' ? `
${data.starNum}
/
${data.starTotal}
` : ''}
`; }()}
const TAG = 'spz-custom-revue-star'; class SPZCustomRevueStar extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.starNum = this.element.getAttribute('starNum'); this.starTotal = this.element.getAttribute('starTotal'); this.showStarText = this.element.getAttribute('showStarText'); this.starColor = this.element.getAttribute('color'); this.interact = this.element.getAttribute('interact'); this.starSize = this.element.getAttribute('starSize') || 14; } mountCallback = () => { this.doRender_({ starTotal: this.starTotal, totalArray: Array.from({ length: Number(this.starTotal) }, (v, k) => k + 1), starNum: this.starNum, showStarText: this.showStarText, starColor: this.starColor, starSize: this.starSize }).then(() => { if (this.interact) { this.addEventListeners_(); } }); } addEventListeners_ = () => { const stars = document.querySelectorAll('.revue-star__star'); stars.forEach(star => { star.addEventListener('click', event => { const starEl = star.closest('.revue-star__star'); const starIndex = Number(starEl.dataset.index); let isHalf = event.offsetX < star.offsetWidth / 2; // rtl if (document.documentElement.getAttribute('dir') === 'rtl') { isHalf = event.offsetX > star.offsetWidth / 2; } const starValue = isHalf ? starIndex - 0.5 : starIndex; this.starClickHandler_({ value: starValue }); }); }); } renderStar = () => { const isRtl = document.documentElement.getAttribute('dir') === 'rtl'; const stars = this.element.querySelectorAll('.revue-star__star'); stars.forEach((star, i) => { const starIndex = i + 1; const starEl = star.querySelector('svg:nth-child(2)'); const isHalf = this.starNum % 1 > 0 && Math.ceil(this.starNum) === starIndex; const isSolid = starIndex <= Math.ceil(this.starNum); starEl.style.display = isSolid ? 'block' : 'none'; if (isHalf) { if (isRtl) { // RTL布局下,如果是半星,显示星星的右半边 starEl.style.clipPath = `polygon(50% 0, 100% 0, 100% 100%, 50% 100%)`; } else { // LTR布局下,如果是半星,显示星星的左半边 starEl.style.clipPath = `polygon(0 0, 50% 0, 50% 100%, 0 100%)`; } } else { starEl.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%)` } }); const showCountEle = this.element.querySelector('#revue-star-show-count'); showCountEle && SPZ.whenApiDefined(showCountEle).then((api) => { api.render({ starNum: this.starNum, starTotal: this.starTotal }); }); } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, { starSize: this.starSize, ...data }, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }) .then(() => { this.starNum = data.starNum; this.renderStar(); }); } starClickHandler_ = (event) => { this.starNum = event.value; this.renderStar(); this.triggerEvent_('change', { value: event.value }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueStar)
${function() { return `
${data.count > 99 ? '99+' : data.count < 1 ? '' : data.count}
`; }()}
const TAG = 'spz-custom-revue-like'; class SPZCustomRevueLike extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.grayColor = this.element.getAttribute('gray_color') || "#BDBDBD"; this.likedColor = this.element.getAttribute('like_color') || "#FFCB44"; this.color = this.grayColor; this.count = this.element.getAttribute('count'); this.revueId = this.element.getAttribute('revue-id'); this.location = this.element.getAttribute('location'); } mountCallback = () => { const likes = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : []; const like = likes.find(item => item.id === this.revueId); if (like) { this.color = like.like_status === 1 ? this.likedColor : this.grayColor; } // 如果location是modal,则找到相同revue-id的list的元素,拿到其count,存在list count变了,但是modal的count没变的情况 if (this.location === 'modal') { const listElement = document.querySelector(`spz-custom-revue-like[revue-id="${this.revueId}"] .revue-like-count`); if (listElement) { this.count = listElement.getAttribute('data-real-count'); } } this.doRender_({ color: this.color, count: this.count }).then(() => { this.addEventListeners_(); if(this.location === 'list') { // modal数量变更,list同步变更 document.addEventListener('like-clicked', (e) => { if (e.detail.location !== this.location && e.detail.id === this.revueId) { this.color = e.detail.like_status === 1 ? this.likedColor : this.grayColor; this.count = e.detail.count; this.element.querySelector('.revue-like__icon').querySelector('svg').setAttribute('fill', this.color); this.element.querySelector('.revue-like__icon').querySelector('svg').querySelector('path').setAttribute('fill', this.color); this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count; this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count); if(this.count > 0){ this.element.querySelector('.revue-like-count').classList.remove('hidden'); }else{ this.element.querySelector('.revue-like-count').classList.add('hidden'); } } }); } }); } addEventListeners_ = () => { const icon = this.element.querySelector('.revue-like__icon'); icon.addEventListener('click', (e) => { e.stopPropagation(); const likeStatus = this.color === this.likedColor ? 0 : 1; this.color = this.color === this.likedColor ? this.grayColor : this.likedColor; this.count = likeStatus === 1 ? parseInt(this.count) + 1 : parseInt(this.count) - 1; icon.querySelector('svg').setAttribute('fill', this.color); icon.querySelector('svg').querySelector('path').setAttribute('fill', this.color); this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count; this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count); if(this.count > 0){ this.element.querySelector('.revue-like-count').classList.remove('hidden'); }else{ this.element.querySelector('.revue-like-count').classList.add('hidden'); } this.postLike(likeStatus); if (this.location === 'modal') { const clickedEvent = new CustomEvent('like-clicked', { detail: { id: this.revueId, like_status: likeStatus, count: this.count, location: this.location } }); document.dispatchEvent(clickedEvent); } }); } setLikeToStorage = (likeToStore) => { if (typeof (Storage) !== 'function') return; const likesInStore = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : []; const reviewIndex = likesInStore.findIndex(item => item.id === likeToStore.id); if (reviewIndex !== -1) { likesInStore[reviewIndex].like_status = likeToStore.like_status; likesInStore[reviewIndex].count = likeToStore.count; } else { likesInStore.push(likeToStore); } sessionStorage.setItem('likes', JSON.stringify(likesInStore)); } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }); } postLike = (likeStatus) => { fetch('/api/comment/like', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ id: this.revueId, status: likeStatus }) }).then((res) => { if (res.status === 200) { this.setLikeToStorage({ id: this.revueId, like_status: likeStatus, count: this.count }); } }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueLike)
${function() { return `
${function() { if(data.imgCover) { if(media.videosrc) { let src = ''; if (media.videosrc) { src = media.videosrc + '.' + media.ext; } const videoDom = `
`; if(!isPC){ return `
${videoDom}
` } return `
${videoDom}
` } else if(media.mp4 || media.hls) { const videoDom = `
`; if(!isPC){ return `
${videoDom}
` } return `
${videoDom}
` } else { if(!isPC){ return `
` }else{ return `
` } } } else { if (media.videosrc) { let src = ''; if (media.videosrc) { src = media.videosrc + '.' + media.ext; } return `
` } else if(media.mp4 || media.hls) { return `
` } else { return `
` } } }()}
`; }()}
const TAG = 'spz-custom-revue-media'; class SPZCustomRevueMedia extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.imgCover = this.element.getAttribute('img-cover') ?? false; this.pc_layout = this.element.getAttribute('pc-layout') ?? ''; // data-images 格式为 xxxx.png?width=1&height=1,xxxx.png?width=1&height=1 const images = this.element.getAttribute('data-images').split(',') || []; const parsedImages = images.map(image => { return this.mediaParse_(image); }); this.images = parsedImages; this.isPC = window.innerWidth > 960; } mountCallback = () => { this.doRender_({ images: this.images, isPC: this.isPC, imgCover: this.imgCover, pc_layout: this.pc_layout }).then(() => { this.addEventListeners_(); }); } addEventListeners_ = () => { const images = this.element.querySelectorAll('.revue-image-item'); images.forEach((image, index) => { image.addEventListener('click', () => { const carousel = document.querySelector('#revue-image-carousel-render'); carousel && SPZ.whenApiDefined(carousel).then((api) => { const width = this.isPC ? 460 : window.innerWidth * 0.9; const height = this.isPC ? 630 : 500; api.render({ images: this.images, index: index, width: width, height: height }); }); }); }); } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }); } mediaParse_ = function (url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.preview_image = url.split('?')[0]; } catch (e) {}; return result; } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueMedia)
${function() { return `
Newest
` }()}
${function() { return `
Newest
Most liked
Highest ratings
Lowest ratings
` }()}
${function() { return `
Newest
Most liked
Highest ratings
Lowest ratings
` }()}
const TAG = 'spz-custom-revue-sort'; class SPZCustomRevueSort extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.isPC = window.innerWidth > 960; this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%'; this.randomStr = Math.random().toString(36).substr(2); this.sectionId = this.element.getAttribute('section-id') || '1638170143953'; this.prefix = this.element.getAttribute('prefix'); } mountCallback = () => { const data = { width: this.width, randomStr: this.randomStr }; this.doRender_(data).then(() => { let revueSortListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-sort-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`); revueSortListRender && SPZ.whenApiDefined(revueSortListRender).then((api) => { api.render(data).then(() => { if (this.isPC) { this.addEventListenersForPC_(); } else { this.addEventListenersForMobile_(); } }); }); }); } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }); } addEventListenersForPC_ = () => { const revueSelectList = this.element.querySelector('.revue_select_list'); const revueSelectItem = this.element.querySelectorAll('.revue_select_item'); const revueSelectSortIcon = this.element.querySelector(`#${this.prefix}-revue_select_sort_icon-${this.sectionId}`); revueSelectItem.forEach(item => { item.addEventListener('click', () => { const sort = item.getAttribute('data-sort'); const direction = item.getAttribute('data-direction'); this.triggerEvent_('sort', { sort, direction }); this.element.querySelector('.revue_select_label').innerText = item.innerText; revueSelectList.classList.remove('revue_select_list_active'); const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`); revueChecked && SPZCore.Dom.removeElement(revueChecked); const revueCheckedClone = revueChecked.cloneNode(true); item.appendChild(revueCheckedClone); const pcDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-pc-dropdown-${this.sectionId}`); if (!revueSelectSortIcon.classList.contains('up_icon')) { return; } revueSelectSortIcon.classList.remove('up_icon'); SPZ.whenApiDefined(pcDropdownEle).then((api) => { api.close(); }); }); }); window.addEventListener('scroll', (e) => { if (!revueSelectSortIcon || !revueSelectSortIcon.classList.contains('up_icon')) { return; } revueSelectSortIcon.classList.remove('up_icon'); SPZ.whenApiDefined(pcDropdownEle).then((api) => { api.close(); }); }); } addEventListenersForMobile_ = () => { const revueSortDropdownRender = document.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`); revueSortDropdownRender && SPZ.whenApiDefined(revueSortDropdownRender).then(async (api) => { await api.render(); const revueSortDropdownItem = document.querySelectorAll(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} .revue_sort_dropdown_item`); revueSortDropdownItem.forEach(item => { item.addEventListener('click', () => { const sort = item.getAttribute('data-sort'); const direction = item.getAttribute('data-direction'); revueSortDropdownItem.forEach((_item)=>{_item.classList.remove('selected')}) item.classList.add('selected'); // 抛出事件 this.triggerEvent_('sort', { sort, direction }); // 移除revue_checked元素,复制一个新的到当前选中的元素 const revueChecked = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} #${this.prefix}-revue_checked`); revueChecked && SPZCore.Dom.removeElement(revueChecked); const revueCheckedClone = revueChecked.cloneNode(true); item.appendChild(revueCheckedClone); const mDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId}`); SPZ.whenApiDefined(mDropdownEle).then((api) => { api.close(); }); }); }); }) } } SPZ.defineElement(TAG, SPZCustomRevueSort)
const TAG = 'spz-custom-revue-flow'; class SpzCustomRevueFlow extends SPZ.BaseElement { constructor(element) { super(element); this.sectionId = this.element.getAttribute('section-id'); this.show_product = ''; this.with_photo = ''; this.limit = ''; this.star_least = ''; this.layout = '' this.wall_pc_num = '' this.wall_mobile_num = '' this.accent_color = '' this.isProductPage = '15' == 1; this.isCollectionPage = '15' == 2; this.isCartPage = '15' == 13; this.lastWidth = window.innerWidth; } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.setupAction_(); const url = new URL(window.location.href); const preview_theme_id = url.searchParams.get('preview_theme_id'); if (preview_theme_id) { this.preview_theme_id = preview_theme_id; } this.commentConfig = {}; this.sort = 'created_at'; this.direction = 'desc'; this.isPC = window.innerWidth > (window.breakpoint || 960); this.appendList = []; this.commentListRes = []; this.cardConfig = window.reviewSettings[this.sectionId]; } render_ = (data={}) => { const {star_least, with_photo, show_product, limit, layout, wall_pc_num, wall_mobile_num, accent_color, fill_strategy, review_insufficient, minimum_comment_num, only_featured, hide_review_section} = this.cardConfig; Object.assign(this, {star_least, with_photo, show_product, limit, layout, wall_pc_num, wall_mobile_num, accent_color, fill_strategy, review_insufficient, minimum_comment_num, only_featured, hide_review_section}); if(this.layout === 'wall'){ this.with_photo = 1; }; this.params = { offset: this.appendList.length || 0, sort_by: this.sort, sort_direction: this.direction, show_reply: 1 || this.commentConfig.show_reply ? 1 : 0, with_photo: this.with_photo, ...data } if(this.fill_strategy == 'store'){ if(this.review_insufficient == 'less_than'){ this.params.fill_min_threshold = minimum_comment_num; }else{ this.params.fill_min_threshold = 1; } this.params.fill_strategy = this.fill_strategy; } const summaryObj = { star_least:this.star_least, product_ids: this.isProductPage ? '' : this.isCartPage ? '' : '', collection_id: this.isCollectionPage ? '' : '', filter_type: (this.isProductPage || this.isCartPage) ? 'product' : this.isCollectionPage ? 'collection' : 'store', fill_strategy: this.params?.fill_strategy || '', only_media: !!this.params.with_photo, only_featured:this.only_featured } if(this.params.fill_min_threshold){ summaryObj.fill_min_threshold = this.params.fill_min_threshold; } Promise.all([ this.fetchSummary_(summaryObj), this.fetchCommentConfig_(), this.fetchCommentList_(this.params) ]).then(response => { const [starCountRes,commentConfigRes, commentListRes] = response; this.commentConfig = commentConfigRes.data; this.commentConfig.show_product = this.show_product; this.commentListRes = commentListRes; this.starCountRes = starCountRes; const showEmpty = this.review_insufficient == 'less_than' && commentListRes?.data?.count < this.minimum_comment_num && this.fill_strategy == 'hide' const showEmpty2 = (!commentListRes?.data?.count || commentListRes?.data?.count*1 == 0) && (this.hide_review_section || this.fill_strategy == 'hide') if(showEmpty2 || showEmpty){ this.renderEmpty_(); return; } if (this.preview_theme_id) { this.fetchThemeConfig_(this.preview_theme_id).then(themeConfig => { if (themeConfig?.star_color) { this.commentConfig.star_color = themeConfig.star_color; } if(this.accent_color && this.accent_color != 'null'){ this.commentConfig.star_color = this.accent_color; } }); } const colums = this.calculateColums_(); this.renderFlowMain_({ config: this.commentConfig, comment: commentListRes.data, column_count: colums }).then(() => { this.renderHeader_({ starData: this.starCountRes.data, listData: this.commentListRes.data, star_color: this.commentConfig.star_color, comment_avg_star: this.commentListRes.data.avg_star, comment_count: this.commentListRes.data?.count, isPC: this.isPC, }); this.renderStarCounts_(this.starCountRes.data); this.addImpression(`[data-section-id="${this.sectionId}"] .revue_container`); this.renderCommentList_({ list: commentListRes.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name },true); }); window.removeEventListener('resize', this.rerenderFn); window.addEventListener('resize', this.rerenderFn); }) .catch(error => { this.renderEmpty_(); console.error('error', error); }); } mountCallback = () => { this.render_() } fetchCommentConfig_ = async () => { const response = await fetch('/api/comment-config'); return response.json(); } fetchSummary_ = async (data) => { const response = await fetch('/api/v1/comments/summary',{ method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); } fetchCommentList_ = async(data) => { const response = await fetch(`/api/v1/comments`,{ method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ ...data, offset: data.offset, show_product:!!this.show_product, star_least:this.star_least, limit:this.limit, sort_by:data.sort_by || 'created_at', sort_direction: data.sort_direction || 'desc', filter_type:(this.isProductPage || this.isCartPage) ? 'product' : this.isCollectionPage ? 'collection' : 'store', show_reply: !!data.show_reply, only_media: !!data.with_photo, product_ids: this.isProductPage ? '' : this.isCartPage ? '' : '', collection_id: this.isCollectionPage ? '' : '', only_featured: this.only_featured, }) }); if(response.status != 200){ return Promise.reject(false); } return response.json(); } fetchThemeConfig_ = async(themeId) => { const response = await fetch(`/api/comment/theme-config?theme_id=${themeId}`); return response.json(); } renderEmpty_ = () => { const holderEl = document.getElementById(`revue_no_data_placeholder_${this.sectionId}`); const skeleton = document.getElementById(`revue_flow_skeleton-${this.sectionId}`); if(skeleton){ skeleton.style.display = 'none'; }; if (window.top !== window.self) { SPZ.whenApiDefined(holderEl).then((api) => { api.render({}, true); }); }else{ holderEl.style.display = 'none'; } } renderFlowMain_ = async (data) => { const mainEle = document.querySelector(`#revue_flow_render-${this.sectionId}`); if (mainEle) { const api = await SPZ.whenApiDefined(mainEle); return api.render({ ...data },true); } } calculateColums_ = () => { let colums = 1; this.isPC = window.innerWidth > (window.breakpoint || 960); if (this.layout === 'grid') { colums = this.isPC ? 4 : 2; } else { colums = this.isPC ? 2 : 1; } if(this.layout == 'wall'){ colums = this.isPC ? (this.wall_pc_num || 4) : (this.wall_mobile_num || 2); } return colums } rerenderFn = (list) => { try{ if(!this?.commentListRes?.data) return; const currentWidth = window.innerWidth; if (currentWidth == this.lastWidth ) { return } else { this.lastWidth = currentWidth; } const throttleHandle = SPZCore.Types.throttle(window,()=>{ let colums = this.calculateColums_(); this.renderFlowMain_({ config: this.commentConfig, comment: this.commentListRes.data, column_count: colums }).then(() => { this.renderHeader_({ starData: this.starCountRes.data, listData: this.commentListRes.data, star_color: this.commentConfig.star_color, comment_avg_star: this.commentListRes.data.avg_star, comment_count: this.commentListRes.data?.count, isPC: this.isPC, }); this.renderStarCounts_(this.starCountRes.data); this.renderCommentList_({ list: this.commentListRes.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true); }); },200) throttleHandle() }catch(e){ console.log(e); } } renderCommentList_ = (data,redo=false) => { if(this.accent_color && this.accent_color != 'null'){ this.commentConfig.star_color = this.accent_color; } const listEle = document.querySelector(`#revue_flow_list-${this.sectionId}`); if (listEle) { const current_list = data.list.list.map((item, index) => { return { ...item, config: this.commentConfig, index: data.sorted ? index : this.appendList.length + index, shop_name: window.SHOPLAZZA.shop.shop_name } }); if (data.sorted) { this.appendList = current_list; SPZ.whenApiDefined(listEle).then((api) => { api.listRender({ count: data.list.count, list: current_list },true); }); } else { let obj = {}; this.appendList = this.appendList.concat(current_list).reduce((cur,next) => { obj[next.id] ? "" : obj[next.id] = true && cur.push(next); return cur; },[]); SPZ.whenApiDefined(listEle).then((api) => { api.listRender({ count: data.list.count, list: current_list },redo); }); } }; this.renderLoadMoreBtn(data.list); } mediaParse_ = function (url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.src = url.split('?')[0]; } catch (e) {}; return result; } impFunc = function (selector, cb) { // 添加自动曝光 const el = document.querySelector(selector); const onImpress = () => { cb(); }; // 元素未曝光时添加曝光事件监听,已曝光则可以立刻触发处理器 if (el && !el.getAttribute('imprsd')) { el.addEventListener('impress', onImpress); } else if (el) { onImpress(); } }; addImpression = function (selector) { this.impFunc(selector, () => { window.sa && window.sa.track('plugin_reviews_masonry_pv', { layout_type: this.layout, level_type: this.star_least, show_number: this.limit, plugin_timestamp: new Date().valueOf().toString(), reviews_num: this.appendList.length }); }); }; addModalImpression = function (selector, params) { this.impFunc(selector, () => { window.sa && window.sa.track('plugin_reviews_modal_pv', { ...params, plugin_timestamp: new Date().valueOf().toString(), }); }); }; setupAction_ = () => { this.registerAction('refresh', async(invocation) => { this.render_({ ...this.params, offset: 0, sort_by: 'created_at', sort_direction: 'desc', show_reply: true, with_photo: false, }) }); this.registerAction('renderTypeChangeList', async(invocation) => { const {type,direction } = invocation.args.data; this.with_photo = type === 'with_photo'; this.direction = direction; this.params = { ...this.params, offset: 0, sort_by: this.sort, sort_direction: this.direction, show_reply: 1, with_photo: this.with_photo }; this.fetchCommentList_(this.params).then(response => { this.renderCommentList_({ sorted: true, list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true); }); }) this.registerAction('renderSortedList', async(invocation) => { const {sort, direction} = invocation.args.data; this.sort = sort; this.direction = direction; const panelId = this.panelId; this.params = { ...this.params, offset: 0, sort_by: this.sort, sort_direction: this.direction, show_reply: 1 , with_photo: this.with_photo } this.fetchCommentList_(this.params).then(response => { this.renderCommentList_({ sorted: true, list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true); }); }); this.registerAction('renderProductCommentModal', async(invocation) => { const id = invocation.args.id; const current = this.appendList?.find(_data => _data.id == id); const modalEle = document.querySelector(`#revueDetailModal-${this.sectionId}`); const imgArr = current.img.map(image => { const width = this.getUrlKey('width', image); const height = this.getUrlKey('height', image); return { width, height, rate: (height/width).toFixed(2)*100, url: image }; }); if (modalEle) { SPZ.whenApiDefined(modalEle).then((api) => { api.renderModalFn({ data: { ...current, img: imgArr }, commentConfig: this.commentConfig, layout: this.layout, level_type: this.star_least, show_number: this.limit }); }); } }); this.registerAction('loadMore', async(invocation) => { this.params = { ...this.params, offset: this.appendList.length, sort_by: this.sort, sort_direction: this.direction, show_reply: 1 || this.commentConfig.show_reply ? 1 : 0, with_photo: this.with_photo } this.fetchCommentList_(this.params).then(response => { this.renderCommentList_({ list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }); }); }); } getUrlKey = (name, url) => { return ( decodeURIComponent( (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec( url ) || [, ""])[1].replace(/\+/g, "%20") ) || null ); } renderHeader_ = (data) => { if(this.accent_color && this.accent_color != 'null'){ data.star_color = this.commentConfig.star_color = this.accent_color; } const headerEle = document.querySelector(`#review-revue-header-${this.sectionId}`); if (headerEle) { SPZ.whenApiDefined(headerEle).then(async (api) => { api.render(data); }); } } renderStarCounts_ = (data) => { const summaryEle = document.querySelector(`#revue-summary-${this.sectionId}`); if (summaryEle) { SPZ.whenApiDefined(summaryEle).then((api) => { api.render({ ...data, star_color: this.commentConfig.star_color }); }); } } renderLoadMoreBtn = (data) => { const loadEle = document.querySelector(`#revue_flow_load_more_render-${this.sectionId}`); if (loadEle) { SPZ.whenApiDefined(loadEle).then((api) => { api.render({ comment: data }, true); }); } } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } unmountCallback(){ } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomRevueFlow)
${function() { const randomStr = Math.random().toString(36).substring(7); const formatDate = value => { let date = new Date(value * 1000); const day = date.toLocaleString('en-US', { day: '2-digit' }); const month = date.toLocaleString('en-US', { month: 'short' }); const year = date.toLocaleString('en-US', { year: 'numeric' }); return month + ' ' + day + ', ' + year; }; const item = data; const config = data.config; const mimic_mobile_style = data?.mimic_mobile_style; return `
${data.img.map(media => { return `
`; }).join('')}
${data.img.map((media, i) => { return `
` }).join('')}
${data.img.map((media, i) => { return `
` }).join('')}
${item.username}
.${item.iso_code_3}
${formatDate(item.created_at)}
Verified
${item.content}
${data.shop_name} reply:
${item.reply && item.reply.length && item.reply[0].content}
${item.product?.title}
`; }()}
const TAG = 'spz-custom-revue-modal'; class SPZCustomRevueModal extends SPZ.BaseElement { constructor(element) { super(element); this.renderedId = ''; this.closeCB = null; this.sectionId = this.element.getAttribute('section-id'); } static deferredMount() { return false; } buildCallback = () => { this.setupAction_(); } mountCallback = () => { } setupAction_ = () => { this.registerAction('renderModal', this.renderModalFn) this.registerAction('closeFn',() => { this?.closeCB?.() }) } mediaParse_ = function (url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.src = url.split('?')[0]; } catch (e) {}; return result; } impFunc = function (selector, cb) { // 添加自动曝光 const el = document.querySelector(selector); const onImpress = () => { cb(); }; // 元素未曝光时添加曝光事件监听,已曝光则可以立刻触发处理器 if (el && !el.getAttribute('imprsd')) { el.addEventListener('impress', onImpress); } else if (el) { onImpress(); } }; addModalImpression = function (selector, params) { this.impFunc(selector, () => { window.sa && window.sa.track('plugin_reviews_modal_pv', { ...params, plugin_timestamp: new Date().valueOf().toString(), }); }); }; renderModalFn(receivedData){ if(!receivedData) return; const { data:current, commentConfig, layout, level_type, show_number, closeCB, mimic_mobile_style, props } = receivedData; try{ if(closeCB){ this.closeCB = () => { closeCB() }; } }catch(e){ console.log(e); }; const commentModalEl = document.querySelector(`#revue-product-comment-modal-${this.sectionId}`); const modalRenderEl = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`); if (!!mimic_mobile_style) { if (commentModalEl) { commentModalEl.classList.add('mobile-wrap'); } if (modalRenderEl) { modalRenderEl.classList.add('w-h-full-h5'); } }; const parsedImages = current?.img?.map(image => { return this.mediaParse_(`${image.url}?width=${image.width}&height=${image.height}`); }); const modalEle = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`); if (modalEle) { SPZ.whenApiDefined(modalEle).then((api) => { api.render({ ...current, img: parsedImages, config: commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name, mimic_mobile_style }, true).then(() => { this.addModalImpression('.revue_modal_container', { id: current.id, username: current.username, content: current.content, star: current.star, is_verified: current.is_verified, is_featured: current.is_featured, anonymous: current.anonymous, iso_code_3: current.iso_code_3, like_count: current.like, layout_type: layout, level_type: level_type, show_number: show_number, }); }).then(()=>{ this.renderedId = current.id }); }); } } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement('spz-custom-revue-modal', SPZCustomRevueModal)
${function() { return `
${function(){ if (media.videosrc) { const src = media.videosrc + '.' + media.ext; return `
` } else if(media.mp4 || media.hls) { return `
` } else { return `` } }()}
`; }()}
const TAG = 'spz-custom-revue-video'; class SPZCustomRevueVideo extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); // data-images 格式为 xxxx.png?width=1&height=1,xxxx.png?width=1&height=1 const images = this.element.getAttribute('data-images').split(',') || []; const parsedImages = images.map(image => { return this.mediaParse_(image); }); this.images = parsedImages; this.isPC = window.innerWidth > 960; } loadVideo = () => { this.doRender_({ images: this.images, isPC: this.isPC }).then(()=>{ this.triggerEvent_('connected', {}); }) } mountCallback = () => { this.loadVideo(); this.registerAction('loadVideo', async(invocation) => { this.loadVideo(); }) } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }); } mediaParse_ = function (url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.preview_image = url.split('?')[0]; } catch (e) {}; return result; } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueVideo)
${function(){ const isPC = data.isPC; const pc_layout = 'single_column'; const m_loading_type = 'modal'; const showCount = data.showCount ?? (!isPC || pc_layout !== 'single_column'); const showSummary = data.showSummary ?? (!isPC || pc_layout !== 'single_column'); const showWriteReview = data.showWriteReview ?? (isPC && pc_layout !== 'single_column'); const viewall = data.viewall ?? (!isPC && m_loading_type === 'modal'); const showType = data.showType ?? ((!isPC || pc_layout !== 'single_column') && !viewall); const showSort = data.showSort ?? ((!isPC || pc_layout !== 'single_column') && !viewall); if(!data.suffix){ data.suffix = '1638170143953' } return `
5-Star Photo Reviews
(${data.comment_count > 500 ? '500+' : (data.comment_count || 0)})
Write a Review
View all
` }()}
const TAG = 'spz-custom-revue-header'; class SPZCustomRevueHeader extends SPZ.BaseElement { constructor(element) { super(element); this.showCount = this.element.getAttribute('show-count'); } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.showCount = this.element.getAttribute('show-count'); this.showSummary = this.element.getAttribute('show-summary'); this.showWriteReview = this.element.getAttribute('show-write-review'); this.showType = this.element.getAttribute('show-type') ; this.showSort = this.element.getAttribute('show-sort') ; this.sectionId = this.element.getAttribute('section-id'); this.viewall = this.element.getAttribute('viewall') ?? false; this.prefix = this.element.getAttribute('prefix'); } mountCallback() { } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } render(data) { const ndata = { ...data, showCount: this.showCount, showSummary: this.showSummary, showWriteReview: this.showWriteReview, showType: this.showType, showSort: this.showSort, } if(this.viewall == 'review'){ ndata.viewall = false } return this.templates_ .findAndRenderTemplate(this.element, ndata, null, true) .then(({el}) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }).then(() => { if(data && Object.keys(data).length > 0) { this.updateRender(data); this.setupSummaryContainerEffects_(data); } }); } updateRender(data) { this.renderStarCounts_(data); this.renderTypeSelect(data); this.renderSortSelect(data); } renderStarCounts_(data) { const renderData = { ...data.starData, star_color: data.star_color, isPC: data.isPC, } const summaryEle = data.isPC ? this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`) : this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`); if(summaryEle) { SPZ.whenApiDefined(summaryEle).then((api) => { api.render(renderData); }); } } renderTypeSelect(data) { const typeSelect = this.element.querySelector(`#${this.prefix}-revue-header-type-${this.sectionId}`); if(typeSelect) { SPZ.whenApiDefined(typeSelect).then((api) => { api.render(data); api.registerAction('headerType_', (invocation) => { this.triggerEvent_('headerType', invocation.args.data); }); }); } } renderSortSelect(data) { const suffix = data.suffix || this.sectionId; const sortSelect = this.element.querySelector(`#${this.prefix}-revue-header-sort-${suffix}`); if(sortSelect) { SPZ.whenApiDefined(sortSelect).then((api) => { api.registerAction('headerSort_', (invocation) => { this.triggerEvent_('headerSort', invocation.args.data); }); }); } } setupSummaryContainerEffects_(data) { if(data.isPC) { this.setupSummaryContainerHover_(); } else { this.setupSummaryContainerTap_(); } } setupSummaryContainerHover_() { const summaryContainer = this.element.querySelector(`#revue-header-summary-container-${this.sectionId}`); const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`); if (!summaryContainer || !summaryEle) return; let isHovering = false; // 鼠标移入容器时显示summary SPZUtils.Event.listen(summaryContainer, 'mouseenter', () => { isHovering = true; summaryEle.removeAttribute('hidden'); const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`); if(selectIcon) { selectIcon.classList.add('up-icon'); } }); // 鼠标移入summary时也保持显示 SPZUtils.Event.listen(summaryEle, 'mouseenter', () => { isHovering = true; }); // 鼠标移出容器时,检查是否还在summary上 SPZUtils.Event.listen(summaryContainer, 'mouseleave', () => { isHovering = false; setTimeout(() => { if (!isHovering) { summaryEle.setAttribute('hidden', 'true'); const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`); if(selectIcon) { selectIcon.classList.remove('up-icon'); } } }, 50); }); // 鼠标移出summary时,检查是否还在容器上 SPZUtils.Event.listen(summaryEle, 'mouseleave', () => { isHovering = false; setTimeout(() => { if (!isHovering) { summaryEle.setAttribute('hidden', 'true'); const selectIcon = summaryEle.querySelector(`#revue-header-summary-icon-${this.sectionId}`); if(selectIcon) { selectIcon.classList.remove('up-icon'); } } }, 50); }); } setupSummaryContainerTap_() { const selectIcon = this.element.querySelector(`#revue-header-summary-icon-${this.sectionId}`); const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`); if(!summaryEle) return; let isTapped = false; // 是否显示summary SPZ.whenApiDefined(summaryEle).then((api) => { api.registerAction('display', () => { if(isTapped) { isTapped = false; summaryEle.removeAttribute('hidden'); selectIcon.classList.add('up-icon'); } else { isTapped = true; summaryEle.setAttribute('hidden', 'true'); selectIcon.classList.remove('up-icon'); } }); }); } } SPZ.defineElement(TAG, SPZCustomRevueHeader);
${function(){ const starOrder = ['one_star', 'two_star', 'three_star', 'four_star', 'five_star']; function sortStarRatings(ratings) { const sortedRatingsArr = []; starOrder.map((star,index) => { sortedRatingsArr.push(index+1); return star; }); return sortedRatingsArr; }; const star_levels = sortStarRatings(data.star_detail).reverse(); return `
${data.comment_avg_star}
Total reviews: ${data.comment_count > 999 ? '999+' : data.comment_count}
${level}
`; }()}
${function() { return `
All
` }()}
${function() { const list = data.listData; return `
All(${list.count})
With Photos(${list.image_count})
` }()}
${function() { const list = data.listData; return `
All(${list.count})
With Photos(${list.image_count})
` }()}
const TAG = 'spz-custom-revue-type'; class SPZCustomRevueType extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.isPC = window.innerWidth > 960; this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%'; this.randomStr = Math.random().toString(36).substr(2); this.sectionId = this.element.getAttribute('section-id') || '1638170143953'; this.prefix = this.element.getAttribute('prefix'); } mountCallback = () => { } render = (data) => { const renderData = { ...data, width: this.width, randomStr: this.randomStr }; return this.templates_ .findAndRenderTemplate(this.element, renderData, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }).then(() => { let revueTypeListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-type-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-type-dropdown-render-${this.sectionId}`); revueTypeListRender && SPZ.whenApiDefined(revueTypeListRender).then((api) => { api.render(renderData).then(() => { if (this.isPC) { this.addEventListenersForPC_(); } else { this.addEventListenersForMobile_(); } }); }); }); } addEventListenersForPC_ = () => { const revueSelectList = this.element.querySelector('.revue_select_list'); const revueSelectItem = this.element.querySelectorAll('.revue_select_item'); const revueSelectTypeIcon = this.element.querySelector(`#${this.prefix}-revue_select_type_icon-${this.sectionId}`); revueSelectItem.forEach(item => { item.addEventListener('click', () => { const type = item.getAttribute('data-type'); const direction = item.getAttribute('data-direction'); this.triggerEvent_('type', { type, direction }); this.element.querySelector('.revue_select_label').innerText = item.innerText; revueSelectList.classList.remove('revue_select_list_active'); const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`); revueChecked && SPZCore.Dom.removeElement(revueChecked); const revueCheckedClone = revueChecked.cloneNode(true); item.appendChild(revueCheckedClone); if (!revueSelectTypeIcon.classList.contains('up_icon')) { return; } const pcDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-pc-dropdown-${this.sectionId}`); revueSelectTypeIcon.classList.remove('up_icon'); SPZ.whenApiDefined(pcDropdownEle).then((api) => { api.close(); }); }); }); window.addEventListener('scroll', (e) => { if (!revueSelectTypeIcon.classList.contains('up_icon')) { return; } revueSelectTypeIcon.classList.remove('up_icon'); SPZ.whenApiDefined(pcDropdownEle).then((api) => { api.close(); }); }); } addEventListenersForMobile_ = () => { const revueTypeDropdownItem = this.element.querySelectorAll(`#${this.prefix}-revue-type-dropdown-${this.sectionId} .revue_type_dropdown_item`); revueTypeDropdownItem.forEach(item => { item.addEventListener('click', () => { const type = item.getAttribute('data-type'); const direction = item.getAttribute('data-direction'); revueTypeDropdownItem.forEach((_item)=>{_item.classList.remove('selected')}) item.classList.add('selected'); // 抛出事件 this.triggerEvent_('type', { type, direction }); // 移除revue_checked元素,复制一个新的到当前选中的元素 const revueChecked = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId} #${this.prefix}-revue_checked`); revueChecked && SPZCore.Dom.removeElement(revueChecked); const revueCheckedClone = revueChecked.cloneNode(true); item.appendChild(revueCheckedClone); const mDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId}`); SPZ.whenApiDefined(mDropdownEle).then((api) => { api.close(); }); }); }); } } SPZ.defineElement(TAG, SPZCustomRevueType)
${function() { const isPercentage = data.show_percentage === 'true' && data.total <= data.show_percentage_num; return `
${!isPercentage ? `${data.count}` : `${data.count / data.total * 100}%`}
` }()}
const TAG = 'spz-custom-revue-progress'; class SPZCustomRevueProgress extends SPZ.BaseElement { constructor(element) { super(element); } static deferredMount() { return false; } buildCallback = () => { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.isPC = window.innerWidth > (window.breakpoint || 960); this.height = '6px'; this.color = this.element.getAttribute('color') || '#000000'; this.show_percentage = 'false'; this.show_percentage_num = 100; this.count = this.element.getAttribute('count'); this.total = this.element.getAttribute('total'); } mountCallback = () => { this.doRender_({ count: Number(this.count), total: Number(this.total), height: this.height, color: this.color, show_percentage: this.show_percentage, show_percentage_num: this.show_percentage_num }).then(() => { }); } doRender_ = (data) => { return this.templates_ .findAndRenderTemplate(this.element, data, null) .then((el) => { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); this.element.appendChild(el); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SPZCustomRevueProgress)
${function() { const item = data.originData; const config = data.config; item.imgArr = item.img.map(image => { const width = getUrlKey('width', image); const height = getUrlKey('height', image); return { width, height, rate: (height/width).toFixed(2)*100, url: image }; }); const randomStr = Math.random().toString(36).substring(7); const formatDate = value => { let date = new Date(value * 1000); const day = date.toLocaleString('en-US', { day: '2-digit' }); const month = date.toLocaleString('en-US', { month: 'short' }); const year = date.toLocaleString('en-US', { year: 'numeric' }); return month + ' ' + day + ', ' + year; }; function getUrlKey(name, url) { return ( decodeURIComponent( (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec( url ) || [, ""])[1].replace(/\+/g, "%20") ) || null ); } let videoMp4 = ''; let videoHls = ''; if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){ videoMp4 = getUrlKey('mp4',item.imgArr[0].url); videoHls = getUrlKey('hls',item.imgArr[0].url); } function mediaParse_(url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.preview_image = url.split('?')[0]; } catch (e) {}; return result; } const parsedImages = item?.img.map(image => { return mediaParse_(image); }); return `
${item.username}
${item.iso_code_3}
${item.content}
${data.shop_name} reply:
${item.reply && item.reply.length && item.reply[0].content}
${formatDate(item.created_at)}
${item.product.title}
`; }()}
${function() { const item = data.originData; const config = data.config; item.imgArr = item.img.map(image => { const width = getUrlKey('width', image); const height = getUrlKey('height', image); return { width, height, rate: (height/width).toFixed(2)*100, url: image }; }); const randomStr = Math.random().toString(36).substring(7); const formatDate = value => { let date = new Date(value * 1000); const day = date.toLocaleString('en-US', { day: '2-digit' }); const month = date.toLocaleString('en-US', { month: 'short' }); const year = date.toLocaleString('en-US', { year: 'numeric' }); return month + ' ' + day + ', ' + year; }; function getUrlKey(name, url) { return ( decodeURIComponent( (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec( url ) || [, ""])[1].replace(/\+/g, "%20") ) || null ); } let videoMp4 = ''; let videoHls = ''; if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){ videoMp4 = getUrlKey('mp4',item.imgArr[0].url); videoHls = getUrlKey('hls',item.imgArr[0].url); } function mediaParse_(url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.preview_image = url.split('?')[0]; } catch (e) {}; return result; } const parsedImages = item?.img.map(image => { return mediaParse_(image); }); return `
+${item.imgArr.length}
${item.content}
${item.product.title}
${item.username}
${formatDate(item.created_at)}
`; }()}
${function() { const item = data.originData; const config = data.config; item.imgArr = item.img.map(image => { const width = getUrlKey('width', image); const height = getUrlKey('height', image); return { width, height, rate: (height/width).toFixed(2)*100, url: image }; }); const randomStr = Math.random().toString(36).substring(7); const formatDate = value => { let date = new Date(value * 1000); const day = date.toLocaleString('en-US', { day: '2-digit' }); const month = date.toLocaleString('en-US', { month: 'short' }); const year = date.toLocaleString('en-US', { year: 'numeric' }); return month + ' ' + day + ', ' + year; }; function getUrlKey(name, url) { return ( decodeURIComponent( (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec( url ) || [, ""])[1].replace(/\+/g, "%20") ) || null ); } let videoMp4 = ''; let videoHls = ''; if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){ videoMp4 = getUrlKey('mp4',item.imgArr[0].url); videoHls = getUrlKey('hls',item.imgArr[0].url); } function mediaParse_(url) { var result = {}; try { url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) { try { result[key] = decodeURIComponent(value); } catch (e) { result[key] = value; } }); result.preview_image = url.split('?')[0]; } catch (e) {}; return result; } const parsedImages = item?.img.map(image => { return mediaParse_(image); }); return `
+${item.imgArr.length}
${item.username}
.${item.iso_code_3}
`; }()}
View more
Wow you reached the bottom
${function(){ return `
No reviews available. The product reviews component has been hidden
Review Flow
` }()}
GET 12% OFF
REGISTER AND GET
12% OFF
Receive updates and exclusive offers (New subscribers ONLY)
Subscribe
0